import cv2
import numpy as np
import time
import torch
from ultralytics import YOLO
import os
import importlib
import matplotlib.pyplot as plt
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
import threading

# 在文件顶部的导入部分添加
import dlib
from scipy.spatial import distance as dist
import math
# 导入必要的模块
import queue
import threading

# 在这里添加init_head_pose_params函数和get_head_pose函数
def init_head_pose_params():
    """初始化头部姿态估计所需的参数"""
    # 3D模型点
    object_pts = np.float32([[6.825897, 6.760612, 4.402142],  # 33左眉左上角
                             [1.330353, 7.122144, 6.903745],  # 29左眉右角
                             [-1.330353, 7.122144, 6.903745],  # 34右眉左角
                             [-6.825897, 6.760612, 4.402142],  # 38右眉右上角
                             [5.311432, 5.485328, 3.987654],  # 13左眼左上角
                             [1.789930, 5.393625, 4.413414],  # 17左眼右上角
                             [-1.789930, 5.393625, 4.413414],  # 25右眼左上角
                             [-5.311432, 5.485328, 3.987654],  # 21右眼右上角
                             [2.005628, 1.409845, 6.165652],  # 55鼻子左上角
                             [-2.005628, 1.409845, 6.165652],  # 49鼻子右上角
                             [2.774015, -2.080775, 5.048531],  # 43嘴左上角
                             [-2.774015, -2.080775, 5.048531],  # 39嘴右上角
                             [0.000000, -3.116408, 6.097667],  # 45嘴中央下角
                             [0.000000, -7.415691, 4.070434]])  # 6下巴角

    # 相机内参
    K = [6.5308391993466671e+002, 0.0, 3.1950000000000000e+002,
         0.0, 6.5308391993466671e+002, 2.3950000000000000e+002,
         0.0, 0.0, 1.0]

    # 相机畸变参数
    D = [7.0834633684407095e-002, 6.9140193737175351e-002, 0.0, 0.0, -1.3073460323689292e+000]

    # 用于绘制的3D点
    reprojectsrc = np.float32([[10.0, 10.0, 10.0],
                               [10.0, 10.0, -10.0],
                               [10.0, -10.0, -10.0],
                               [10.0, -10.0, 10.0],
                               [-10.0, 10.0, 10.0],
                               [-10.0, 10.0, -10.0],
                               [-10.0, -10.0, -10.0],
                               [-10.0, -10.0, 10.0]])

    # 绘制线条的点对
    line_pairs = [[0, 1], [1, 2], [2, 3], [3, 0],
                  [4, 5], [5, 6], [6, 7], [7, 4],
                  [0, 4], [1, 5], [2, 6], [3, 7]]

    # 转换相机矩阵和畸变系数为适当格式
    cam_matrix = np.array(K).reshape(3, 3).astype(np.float32)
    dist_coeffs = np.array(D).reshape(5, 1).astype(np.float32)

    return object_pts, cam_matrix, dist_coeffs, reprojectsrc, line_pairs


def get_head_pose(landmarks, object_pts, cam_matrix, dist_coeffs, reprojectsrc):
    """
    计算头部姿态（欧拉角）

    参数:
        landmarks: dlib检测到的68个面部关键点
        object_pts: 3D模型点
        cam_matrix: 相机内参矩阵
        dist_coeffs: 畸变系数
        reprojectsrc: 用于绘制的3D点

    返回:
        reprojectdst: 投影点
        euler_angle: 欧拉角 (pitch, yaw, roll)
    """
    # 提取需要的2D关键点
    # 17左眉左上角/21左眉右角/22右眉左上角/26右眉右上角/36左眼左上角/39左眼右上角/42右眼左上角/
    # 45右眼右上角/31鼻子左上角/35鼻子右上角/48左上角/54嘴右上角/57嘴中央下角/8下巴角
    image_pts = np.float32([landmarks[17], landmarks[21], landmarks[22], landmarks[26],
                            landmarks[36], landmarks[39], landmarks[42], landmarks[45],
                            landmarks[31], landmarks[35], landmarks[48], landmarks[54],
                            landmarks[57], landmarks[8]])

    # 求解旋转和平移矩阵
    _, rotation_vec, translation_vec = cv2.solvePnP(object_pts, image_pts, cam_matrix, dist_coeffs)

    # 重投影3D点到2D
    reprojectdst, _ = cv2.projectPoints(reprojectsrc, rotation_vec, translation_vec, cam_matrix, dist_coeffs)
    reprojectdst = tuple(map(tuple, reprojectdst.reshape(8, 2)))

    # 计算欧拉角
    rotation_mat, _ = cv2.Rodrigues(rotation_vec)  # 罗德里格斯公式
    pose_mat = cv2.hconcat((rotation_mat, translation_vec))  # 水平拼接
    _, _, _, _, _, _, euler_angle = cv2.decomposeProjectionMatrix(pose_mat)

    # 转换欧拉角为度数
    pitch, yaw, roll = [math.radians(_) for _ in euler_angle]

    # 修正角度计算
    pitch = math.degrees(math.asin(math.sin(pitch)))
    roll = -math.degrees(math.asin(math.sin(roll)))
    yaw = math.degrees(math.asin(math.sin(yaw)))

    return reprojectdst, (pitch, yaw, roll)

def calculate_head_pose_bpa(head_down_duration, nodding_detected):
    """计算头部姿态的基本概率分配"""
    bpa = {'正常': 0.0, '轻度疲劳': 0.0, '严重疲劳': 0.0, '不确定': 0.0}

    # 如果检测到点头，直接判定为严重疲劳
    if nodding_detected:
        bpa['严重疲劳'] = 0.8
        bpa['轻度疲劳'] = 0.1
        bpa['不确定'] = 0.1
        return bpa

    # 根据头部下垂持续时间判断疲劳程度
    if head_down_duration < 0.5:
        bpa['正常'] = 0.9
        bpa['不确定'] = 0.1
    elif head_down_duration < 1.0:
        bpa['正常'] = 0.5
        bpa['轻度疲劳'] = 0.4
        bpa['不确定'] = 0.1
    elif head_down_duration < 2.0:
        bpa['轻度疲劳'] = 0.7
        bpa['严重疲劳'] = 0.2
        bpa['不确定'] = 0.1
    else:
        bpa['轻度疲劳'] = 0.2
        bpa['严重疲劳'] = 0.7
        bpa['不确定'] = 0.1

    return bpa


def calculate_perclos_bpa(perclos_value):
    """计算PERCLOS指标的基本概率分配"""
    bpa = {'正常': 0.0, '轻度疲劳': 0.0, '严重疲劳': 0.0, '不确定': 0.0}

    # PERCLOS值通常在0-1之间，值越高表示疲劳程度越高
    if perclos_value < 0.15:
        bpa['正常'] = 0.8
        bpa['轻度疲劳'] = 0.1
        bpa['不确定'] = 0.1
    elif perclos_value < 0.25:
        bpa['正常'] = 0.5
        bpa['轻度疲劳'] = 0.4
        bpa['不确定'] = 0.1
    elif perclos_value < 0.4:
        bpa['轻度疲劳'] = 0.7
        bpa['严重疲劳'] = 0.2
        bpa['不确定'] = 0.1
    else:
        bpa['轻度疲劳'] = 0.2
        bpa['严重疲劳'] = 0.7
        bpa['不确定'] = 0.1

    return bpa


def calculate_eye_closure_bpa(eye_closed_duration):
    """计算闭眼持续时间的基本概率分配"""
    bpa = {'正常': 0.0, '轻度疲劳': 0.0, '严重疲劳': 0.0, '不确定': 0.0}

    # 闭眼持续时间单位为秒
    if eye_closed_duration < 0.5:
        bpa['正常'] = 0.9
        bpa['不确定'] = 0.1
    elif eye_closed_duration < 1.0:
        bpa['正常'] = 0.5
        bpa['轻度疲劳'] = 0.4
        bpa['不确定'] = 0.1
    elif eye_closed_duration < 2.0:
        bpa['轻度疲劳'] = 0.7
        bpa['严重疲劳'] = 0.2
        bpa['不确定'] = 0.1
    else:
        bpa['轻度疲劳'] = 0.2
        bpa['严重疲劳'] = 0.7
        bpa['不确定'] = 0.1

    return bpa


def calculate_blink_rate_bpa(blinks_per_minute):
    """计算眨眼频率的基本概率分配"""
    bpa = {'正常': 0.0, '轻度疲劳': 0.0, '严重疲劳': 0.0, '不确定': 0.0}

    # 正常人每分钟眨眼15-20次
    if 12 <= blinks_per_minute <= 20:
        bpa['正常'] = 0.8
        bpa['不确定'] = 0.2
    elif 8 <= blinks_per_minute < 12 or 20 < blinks_per_minute <= 25:
        bpa['正常'] = 0.4
        bpa['轻度疲劳'] = 0.5
        bpa['不确定'] = 0.1
    elif blinks_per_minute < 8 or blinks_per_minute > 25:
        bpa['轻度疲劳'] = 0.3
        bpa['严重疲劳'] = 0.6
        bpa['不确定'] = 0.1

    return bpa


def calculate_yawn_bpa(yawn_count_recent):
    """计算最近打哈欠次数的基本概率分配"""
    bpa = {'正常': 0.0, '轻度疲劳': 0.0, '严重疲劳': 0.0, '不确定': 0.0}

    # 最近3分钟内的打哈欠次数
    if yawn_count_recent == 0:
        bpa['正常'] = 0.9
        bpa['不确定'] = 0.1
    elif yawn_count_recent == 1:
        bpa['正常'] = 0.3
        bpa['轻度疲劳'] = 0.6
        bpa['不确定'] = 0.1
    elif yawn_count_recent == 2:
        bpa['轻度疲劳'] = 0.7
        bpa['严重疲劳'] = 0.2
        bpa['不确定'] = 0.1
    else:
        bpa['轻度疲劳'] = 0.2
        bpa['严重疲劳'] = 0.7
        bpa['不确定'] = 0.1

    return bpa


def ds_combine(m1, m2):
    """
    实现D-S证据理论的Dempster组合规则

    参数:
        m1: 第一个证据源的基本概率分配
        m2: 第二个证据源的基本概率分配

    返回:
        组合后的基本概率分配
    """
    result = {}
    conflict = 0

    # 计算冲突因子K
    for k1 in m1.keys():
        for k2 in m2.keys():
            if k1 != '不确定' and k2 != '不确定' and k1 != k2:
                conflict += m1[k1] * m2[k2]

    # 如果完全冲突，返回平均值
    if conflict == 1:
        for k in set(list(m1.keys()) + list(m2.keys())):
            result[k] = (m1.get(k, 0.0) + m2.get(k, 0.0)) / 2
        return result

    # 计算组合后的基本概率分配
    for k1 in m1.keys():
        for k2 in m2.keys():
            if k1 == '不确定':
                key = k2
            elif k2 == '不确定':
                key = k1
            elif k1 == k2:
                key = k1
            else:
                continue

            if key not in result:
                result[key] = 0

            result[key] += m1[k1] * m2[k2]

    # 归一化
    for k in result.keys():
        result[k] = result[k] / (1 - conflict)

    return result


def combine_all_evidence(evidence_list):
    """
    组合多个证据源

    参数:
        evidence_list: 多个证据源的基本概率分配列表

    返回:
        组合后的基本概率分配
    """
    if not evidence_list:
        return {}

    result = evidence_list[0]
    for i in range(1, len(evidence_list)):
        result = ds_combine(result, evidence_list[i])

    return result

class FatigueDetector:
    """疲劳检测器类"""

    def __init__(self, model_path=None):
        """初始化疲劳检测器

        参数:
            model_path: YOLO模型路径，如果为None则使用默认模型
        """
        # 记录检测开始时间
        self.detection_start_time = time.time()

        # 添加视频帧率属性
        self.video_fps = 30  # 默认帧率为30fps

        # 添加这些行
        self.frame_queue = queue.Queue(maxsize=2)  # 最多保留2帧
        self.result_queue = queue.Queue(maxsize=2)  # 最多保留2个结果
        self.processing_thread = None
        self.is_processing = False
        self.last_processed_frame = None

        # 初始化处理帧计数和时间
        self._last_process_time = time.time()
        self._frame_times = []  # 用于计算平均处理时间

        # 初始化左右眼状态
        self.left_eye_state = "睁开"
        self.right_eye_state = "睁开"
        self.left_eye_open_confidence = 0.0
        self.left_eye_closed_confidence = 0.0
        self.right_eye_open_confidence = 0.0
        self.right_eye_closed_confidence = 0.0

        # 为了兼容性添加总体眼睛状态置信度
        self.eye_open_confidence = 0.0
        self.eye_closed_confidence = 0.0

        # 初始化左右眼时间记录
        self.left_eye_open_time = time.time()
        self.left_eye_closed_time = time.time()
        self.right_eye_open_time = time.time()
        self.right_eye_closed_time = time.time()

        # 初始化头部姿态检测相关变量
        self.face_detector = dlib.get_frontal_face_detector()
        self.landmark_predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")

        # 初始化头部姿态估计参数
        self.object_pts, self.cam_matrix, self.dist_coeffs, self.reprojectsrc, self.line_pairs = init_head_pose_params()

        # 点头检测相关变量
        self.head_pose = {'pitch': 0.0, 'yaw': 0.0, 'roll': 0.0}
        self.head_direction = "正向"
        self.nodding_detected = False
        self.nodding_count = 0
        self.last_nodding_time = 0
        self.pitch_history = []  # 存储pitch角度历史
        self.pitch_history_max_size = 30  # 保存最近30帧的pitch值
        self.nod_threshold = 15.0  # 点头检测阈值
        self.nod_consecutive_frames = 3  # 连续帧阈值
        self.nod_counter = 0  # 点头帧计数器
        self.head_down_duration = 0.0  # 头部下垂持续时间
        self.head_down_start_time = 0  # 头部下垂开始时间
        self.max_head_down_duration = 0.0  # 最大头部下垂持续时间

        # 添加时间窗口平滑相关属性
        self.fatigue_score_history = []  # 存储(时间戳, 疲劳分数)元组
        self.smoothing_window_size = 30.0  # 30秒的时间窗口

        # 添加滞后阈值相关属性
        self.NORMAL_TO_MILD = 0.35  # 正常→轻度疲劳的上升阈值
        self.MILD_TO_NORMAL = 0.25  # 轻度疲劳→正常的下降阈值
        self.MILD_TO_SEVERE = 0.65  # 轻度疲劳→严重疲劳的上升阈值
        self.SEVERE_TO_MILD = 0.55  # 严重疲劳→轻度疲劳的下降阈值

        # 添加连续帧判断相关属性
        self.consecutive_level_candidate = 0  # 候选警报级别
        self.consecutive_frame_count = 0  # 连续帧计数
        self.FRAMES_TO_CHANGE = 3  # 需要连续3帧满足条件才改变状态

        # 原始疲劳分数（未经平滑处理）
        self.raw_fatigue_score = 0.0

        try:
            # 设置matplotlib支持中文显示
            plt.rcParams['font.sans-serif'] = ['Microsoft YaHei', 'SimHei', 'SimSun', 'Arial Unicode MS']
            plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题

            # 设置默认模型路径
            if model_path is None:
                model_path = r"E:\b-s\yolov10\yolov10-main\runs\detect\train_v1010\weights\best.pt"

            # 检查模型文件是否存在
            if not os.path.exists(model_path):
                raise FileNotFoundError(f"模型文件不存在: {model_path}")

            # 设置设备
            self.device = 'cuda:0' if torch.cuda.is_available() else 'cpu'
            print(f"使用设备: {self.device}")

            # 加载YOLO模型
            self.yolo_model = YOLO(model_path)
            # print(f"YOLO模型加载成功！使用设备: {self.device}")

            # YOLO模型优化 - 新增代码开始
            if self.device.startswith('cuda'):
                # 确保模型在GPU上
                self.yolo_model.to(self.device)

                # 预热模型以减少第一次推理的延迟
                print("正在预热模型...")
                dummy_input = torch.zeros((1, 3, 384, 384), device=self.device)


                # 使用一个小尺寸的图像进行预热
                warmup_img = np.zeros((384, 384, 3), dtype=np.uint8)
                for _ in range(3):  # 预热3次
                    _ = self.yolo_model.predict(
                        source=warmup_img,
                        conf=0.25,
                        iou=0.5,
                        device=self.device,
                        verbose=False
                    )
                    # 尝试优化模型内存使用
                    torch.cuda.empty_cache()
                # YOLO模型优化 - 新增代码结束

                print(f"YOLO模型加载成功！使用设备: {self.device}")

            # 初始化帧计数
            self.frame_count = 0

            # 初始化检测选项
            self.detect_eyes = True
            self.detect_mouth = True

            # 设置默认性能模式
            self.set_performance_mode('balanced')

            # 添加PERCLOS相关变量
            self.eye_closed_frames = 0
            self.total_frames = 0
            self.perclos_window = 150  # 计算PERCLOS的时间窗口(帧数)
            self.perclos_value = 0.0

            # 添加持续闭眼时间跟踪
            self.current_eye_closed_duration = 0.0
            self.max_eye_closed_duration = 0.0

            # 添加复合疲劳行为检测
            self.last_yawn_time = 0
            self.last_long_blink_time = 0
            self.compound_fatigue_events = []  # 记录复合疲劳事件

            # 添加疲劳警报级别
            self.fatigue_alert_level = 0  # 0-正常，1-轻度警告，2-严重警告

            # 添加数据记录用于生成图表
            self.fatigue_scores_history = []
            self.perclos_history = []
            self.eye_closed_duration_history = []
            self.timestamps = []
            self.alert_level_history = []

            # 添加嘴巴状态文本
            self.mouth_status_text = ""

            # 添加眨眼频率计算
            self.blink_durations = []
            self.blink_count = 0
            self.yawn_count = 0

            # 添加打哈欠时间记录
            self.yawn_times = []
            self.yawn_detected = False

            # 添加嘴巴动作计数
            self.mouth_movement_count = 0

            # 初始化当前指标字典
            self.current_indicators = {}

            # 初始化运动检测相关变量
            self.prev_frame = None
            self.enable_motion_detection = False
            self.motion_threshold = 0.02
            self.motion_detection_interval = 5
            self.is_camera_mode = False

            # 添加打哈欠阈值
            self.yawn_threshold = 1.5  # 默认打哈欠阈值为1.5秒


            # 添加眼睛状态历史
            self.eye_state_history = []
            self.eye_state_history_size = 10

            # 添加这一行来优化YOLO推理
            self.optimize_yolo_inference()

        except Exception as e:
            print(f"初始化疲劳检测器时出错: {str(e)}")
            raise

    def start_async_processing(self):
        """启动异步处理线程"""
        if self.processing_thread is not None and self.processing_thread.is_alive():
            return

        self.is_processing = True
        self.processing_thread = threading.Thread(target=self._process_frames_worker)
        self.processing_thread.daemon = True
        self.processing_thread.start()
        print("异步处理线程已启动")

    def _process_frames_worker(self):
        """帧处理工作线程"""
        while self.is_processing:
            try:
                # 非阻塞方式获取帧
                try:
                    frame = self.frame_queue.get(timeout=0.1)
                except queue.Empty:
                    continue

                # 处理帧
                result_frame = self.process_frame(frame)

                # 将结果放入结果队列
                if self.result_queue.full():
                    try:
                        self.result_queue.get_nowait()  # 丢弃旧结果
                    except queue.Empty:
                        pass
                self.result_queue.put(result_frame)

            except Exception as e:
                print(f"异步处理帧时出错: {str(e)}")

        print("处理线程已停止")

    def add_frame(self, frame):
        """添加帧到处理队列"""
        if not self.is_processing:
            self.start_async_processing()

        # 如果队列已满，清空整个队列并只保留最新帧
        if self.frame_queue.full():
            try:
                while not self.frame_queue.empty():
                    self.frame_queue.get_nowait()
            except queue.Empty:
                pass

        self.frame_queue.put(frame)

    def get_latest_result(self):
        """获取最新处理结果"""
        try:
            return self.result_queue.get_nowait()
        except queue.Empty:
            return self.last_processed_frame  # 如果没有新结果，返回上一次处理的帧

    def stop_async_processing(self):
        """停止异步处理"""
        self.is_processing = False
        if self.processing_thread and self.processing_thread.is_alive():
            self.processing_thread.join(timeout=1.0)
        print("异步处理已停止")

    def draw_landmarks(self, frame, landmarks, original_size=None, process_size=None):
        """正确绘制面部关键点"""
        if landmarks is None:
            return frame

        # 如果有尺寸变换，计算缩放比例
        scale_x, scale_y = 1.0, 1.0
        if original_size and process_size:
            scale_x = original_size[1] / process_size[0]
            scale_y = original_size[0] / process_size[1]

        # 绘制关键点
        for (x, y) in landmarks:
            # 应用缩放比例
            x_scaled = int(x * scale_x)
            y_scaled = int(y * scale_y)
            cv2.circle(frame, (x_scaled, y_scaled), 1, (0, 0, 255), -1)

        return frame

    def get_landmarks(self, frame):
        """获取人脸关键点"""
        gray = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY)
        faces = self.face_detector(gray)

        if len(faces) > 0:
            # 获取第一个检测到的人脸
            face = faces[0]
            # 获取68个关键点
            shape = self.landmark_predictor(gray, face)

            # 将关键点转换为numpy数组
            landmarks = []
            for i in range(68):
                x = shape.part(i).x
                y = shape.part(i).y
                landmarks.append((x, y))

            return np.array(landmarks)

        return None

    def calculate_pitch_angle(self, landmarks):
        """计算头部俯仰角(pitch)"""
        if landmarks is None:
            return 0.0

        # 获取关键点
        nose_tip = landmarks[30]  # 鼻尖
        left_eye = landmarks[36]  # 左眼内角
        right_eye = landmarks[45]  # 右眼内角
        chin = landmarks[8]  # 下巴

        # 计算眼睛中心点
        eye_center = ((left_eye[0] + right_eye[0]) / 2, (left_eye[1] + right_eye[1]) / 2)

        # 计算俯仰角
        # 当头部下垂时，鼻尖到下巴的距离会变短，眼睛中心到下巴的距离会增加
        nose_chin_dist = dist.euclidean(nose_tip, chin)
        eye_chin_dist = dist.euclidean(eye_center, chin)

        # 正常状态下的比例
        normal_ratio = 0.85  # 这个值需要根据实际情况调整

        # 计算当前比例
        current_ratio = nose_chin_dist / (eye_chin_dist + 1e-5)  # 避免除零错误

        # 将比例转换为角度
        pitch_angle = -1*(normal_ratio - current_ratio) * 90  # 将比例变化映射到角度

        return pitch_angle

    def calculate_yaw_angle(self, landmarks):
        """计算头部偏航角(yaw)"""
        if landmarks is None:
            return 0.0

        # 获取关键点
        left_eye = landmarks[36]  # 左眼内角
        right_eye = landmarks[45]  # 右眼内角
        nose_tip = landmarks[30]  # 鼻尖

        # 计算眼睛中心点
        eye_center = ((left_eye[0] + right_eye[0]) / 2, (left_eye[1] + right_eye[1]) / 2)

        # 计算鼻尖到眼睛中心的水平偏移
        horizontal_offset = nose_tip[0] - eye_center[0]

        # 计算眼睛间距
        eye_distance = dist.euclidean(left_eye, right_eye)

        # 将偏移归一化，并映射到角度
        yaw_angle = (horizontal_offset / (eye_distance + 1e-5)) * 45

        return yaw_angle

    def calculate_roll_angle(self, landmarks):
        """计算头部翻滚角(roll)"""
        if landmarks is None:
            return 0.0

        # 获取左右眼关键点
        left_eye = landmarks[36]  # 左眼内角
        right_eye = landmarks[45]  # 右眼内角

        # 计算眼睛连线与水平线的夹角
        dx = right_eye[0] - left_eye[0]
        dy = right_eye[1] - left_eye[1]

        # 计算角度（弧度）
        angle_rad = math.atan2(dy, dx)
        # 转换为角度
        angle_deg = math.degrees(angle_rad)

        return angle_deg

    def detect_head_pose(self, frame):
        """检测头部姿态"""
        try:
            # 保存原始尺寸
            original_size = frame.shape[:2]
            process_size = None

            # 检查是否需要调整大小进行处理
            if hasattr(self, 'process_size'):
                process_size = self.process_size

            # 获取面部特征点
            landmarks = self.get_landmarks(frame)

            if landmarks is None:
                return None

            # 计算头部姿态
            reprojectdst, (pitch, yaw, roll) = get_head_pose(
                landmarks, self.object_pts, self.cam_matrix, self.dist_coeffs, self.reprojectsrc
            )

            # 更新头部姿态
            self.head_pose = {'pitch': pitch, 'yaw': yaw, 'roll': roll}

            # 记录pitch角度历史
            current_time = time.time()
            self.pitch_history.append((current_time, pitch))

            # 保持历史记录大小
            while len(self.pitch_history) > self.pitch_history_max_size:
                self.pitch_history.pop(0)

            # 根据pitch角度判断头部方向
            if pitch > 15:  # 头部上仰
                self.head_direction = "上仰"
                # 重置头部下垂时间
                self.head_down_duration = 0.0
                self.head_down_start_time = 0
            elif pitch < -15:  # 头部下垂
                self.head_direction = "下垂"

                # 记录头部下垂持续时间
                if self.head_down_start_time == 0:
                    self.head_down_start_time = current_time

                self.head_down_duration = current_time - self.head_down_start_time

                # 更新最大下垂持续时间
                if self.head_down_duration > self.max_head_down_duration:
                    self.max_head_down_duration = self.head_down_duration
            else:
                self.head_direction = "正向"
                # 重置头部下垂时间
                self.head_down_duration = 0.0
                self.head_down_start_time = 0

            # 检测点头动作
            self.detect_nodding(current_time)

            # 使用新的绘制方法绘制关键点
            if original_size != frame.shape[:2] and process_size:
                # 如果帧被调整了大小，传递原始尺寸和处理尺寸
                self.draw_landmarks(frame, landmarks, original_size, process_size)
            else:
                # 如果没有尺寸调整，直接绘制
                self.draw_landmarks(frame, landmarks)

            return landmarks

        except Exception as e:
            print(f"头部姿态检测出错: {str(e)}")
            return None

    def draw_head_pose_lines(self, frame, reprojectdst):
        """
        在图像上绘制头部姿态线条的方法

        此方法已被禁用，不再绘制头部姿态线条

        参数:
            frame: 要绘制的图像
            reprojectdst: 投影点
        """
        # 此方法已被禁用，不再绘制头部姿态线条
        pass

    def detect_nodding(self, current_time):
        """
        检测点头动作
        使用基于阈值的方法检测点头
        """
        if len(self.pitch_history) < 10:  # 需要足够的历史数据
            return False

        # 如果距离上次点头不到2秒，不检测新的点头
        if current_time - self.last_nodding_time < 2.0 and self.last_nodding_time > 0:
            return False

        # 获取最近的pitch角度数据
        recent_pitch = [p for _, p in self.pitch_history[-10:]]

        # 计算pitch角度变化
        pitch_range = max(recent_pitch) - min(recent_pitch)
        current_pitch = recent_pitch[-1]

        # 检测点头特征：
        # 1. 当前头部姿态为下垂
        # 2. 最近的pitch角度变化范围大于阈值
        if current_pitch < -self.nod_threshold and pitch_range > 20.0:
            self.nod_counter += 1

            # 如果连续多帧满足条件，则判定为点头
            if self.nod_counter >= self.nod_consecutive_frames:
                self.nodding_count += 1
                self.nodding_detected = True
                self.last_nodding_time = current_time
                self.nod_counter = 0

                # 添加到疲劳事件
                if hasattr(self, 'compound_fatigue_events'):
                    self.compound_fatigue_events.append(("点头瞌睡", current_time))

                print(f"检测到点头动作! 总点头次数: {self.nodding_count}")
                return True
        else:
            # 重置计数器
            self.nod_counter = 0

        return False

    def get_head_direction(self):
        """获取头部方向"""
        return self.head_direction

    def get_yaw_angle(self):
        """获取偏航角"""
        return self.head_pose['yaw']

    def get_head_pose(self):

        """获取头部姿态"""
        return self.head_pose

    def set_camera_resolution(self, width=1280, height=720):
        """
        为摄像头设置合适的分辨率

        参数:
            width: 宽度，默认640
            height: 高度，默认480
        """
        self.camera_resolution = (width, height)
        print(f"已设置摄像头分辨率: {width}x{height}")

        # 更新处理尺寸，确保与摄像头分辨率匹配
        if hasattr(self, 'process_size'):
            # 保持处理尺寸不大于摄像头分辨率
            process_width = min(self.process_size[0], width)
            process_height = min(self.process_size[1], height)
            self.process_size = (process_width, process_height)
            print(f"已调整处理尺寸为: {process_width}x{process_height}")

    def optimize_for_camera(self, is_camera=True):
        """
        针对摄像头模式进行专门优化

        参数:
            is_camera: 是否为摄像头模式
        """
        self.is_camera_mode = is_camera

        if is_camera:
            # 摄像头模式专用优化
            # 1. 降低处理分辨率 (确保是32的倍数)
            self.process_size = (320, 256)  # 修改为256而不是240

            # 2. 增加跳帧频率
            self.detection_interval = 3  # 每3帧处理一次

            # 3. 减少PERCLOS窗口大小
            self.perclos_window = 60  # 约2秒(30fps)

            # 4. 启用更激进的运动检测
            self.enable_motion_detection = True
            self.motion_threshold = 0.03  # 提高阈值
            self.motion_detection_interval = 3  # 更频繁检测运动

            print("已启用摄像头模式优化：")
            print(f"- 处理分辨率: {self.process_size}")
            print(f"- 检测间隔: {self.detection_interval}帧")
            print(f"- PERCLOS窗口: {self.perclos_window}帧")
            print(f"- 运动检测阈值: {self.motion_threshold}")
        else:
            # 视频文件模式，使用默认设置
            self.enable_motion_detection = False
            self.perclos_window = 150  # 恢复默认值
            print("已禁用摄像头模式优化")

    def set_performance_mode(self, mode='balanced'):
        """设置性能模式

        参数:
            mode: 'high_speed' - 优先速度
                  'high_accuracy' - 优先准确度
                  'balanced' - 平衡模式(默认)
        """
        try:
            if mode == 'high_speed':
                # 确保尺寸是32的倍数
                self.process_size = (320, 256)  # 修改为256而不是240
                self.detection_interval = 3
                self.use_half = False
            elif mode == 'high_accuracy':
                # 确保尺寸是32的倍数
                self.process_size = (640, 480)  # 这个已经是32的倍数
                self.detection_interval = 1
                self.use_half = False
            else:  # balanced
                # 确保尺寸是32的倍数
                self.process_size = (480, 384)  # 修改为384而不是360
                self.detection_interval = 2
                self.use_half = False

            # 初始化帧计数器
            self.frame_counter = 0
            # 保存最后一次检测结果
            self.last_detection_results = None

            print(f"已设置性能模式: {mode}, 处理分辨率: {self.process_size}, 检测间隔: {self.detection_interval}")
        except Exception as e:
            print(f"设置性能模式时出错: {str(e)}")

    def is_yawning(self, mouth_open_duration, mouth_open_confidence, mouth_movement_count):
        """改进的打哈欠识别算法，综合多种特征和时序分析"""
        # 特征权重
        weights = {
            'mar': 0.25,  # 嘴部纵横比
            'duration': 0.25,  # 持续时间
            'temporal_pattern': 0.25,  # 时序模式
            'contour': 0.15,  # 嘴唇内轮廓
            'frequency': 0.10  # 变化频率
        }

        scores = {}

        # 1. MAR特征(如果有landmarks)
        if hasattr(self, 'face_landmarks') and self.face_landmarks is not None:
            mar = self.calculate_MAR(self.face_landmarks)
            scores['mar'] = min(1.0, max(0.0, (mar - 0.4) / 0.3))  # 归一化到0-1
        else:
            # 使用嘴巴张开置信度作为替代
            scores['mar'] = min(1.0, mouth_open_confidence)

        # 2. 持续时间特征
        if mouth_open_duration < 1.0:
            scores['duration'] = 0.0
        elif mouth_open_duration < 2.0:
            scores['duration'] = (mouth_open_duration - 1.0)
        else:
            scores['duration'] = min(1.0, 0.8 + (mouth_open_duration - 2.0) / 10.0)

        # 3. 时序模式分析
        scores['temporal_pattern'] = self.analyze_mouth_temporal_pattern()

        # 4. 嘴唇内轮廓特征(如果有mouth_region)
        if hasattr(self, 'mouth_region') and self.mouth_region is not None:
            area, circularity = self.calculate_mouth_inner_contour(self.mouth_region)
            # 打哈欠时面积大且圆度高
            area_score = min(1.0, area / 2000)  # 假设2000是典型打哈欠的面积
            scores['contour'] = 0.7 * area_score + 0.3 * circularity
        else:
            scores['contour'] = 0.5  # 默认中等值

        # 5. 变化频率特征
        if hasattr(self, 'calculate_mouth_change_frequency'):
            change_frequency = self.calculate_mouth_change_frequency(3.0)
            if change_frequency < 0.3:
                scores['frequency'] = 1.0  # 变化非常少，很可能是打哈欠
            elif change_frequency < 0.7:
                scores['frequency'] = 0.8  # 变化较少
            elif change_frequency < 1.5:
                scores['frequency'] = 0.4  # 变化中等
            else:
                scores['frequency'] = 0.0  # 变化频繁，可能是说话
        else:
            # 使用mouth_movement_count作为替代
            if mouth_movement_count <= 1:
                scores['frequency'] = 1.0
            elif mouth_movement_count == 2:
                scores['frequency'] = 0.7
            elif mouth_movement_count == 3:
                scores['frequency'] = 0.3
            else:
                scores['frequency'] = 0.0

        # 计算加权总分
        total_score = sum(scores[k] * weights[k] for k in scores)

        # 判断阈值
        is_yawn = total_score > 0.65

        # 特殊规则：如果持续时间非常长且张开程度大，直接判定为打哈欠
        if mouth_open_duration > 2.0 and mouth_open_confidence > 0.85:
            is_yawn = True
            total_score = max(total_score, 0.85)

        return is_yawn, total_score

    def calculate_mouth_change_frequency(self, time_window=3.0):
        """计算指定时间窗口内嘴巴状态变化的频率"""
        if not hasattr(self, 'mouth_state_change_times'):
            return 0.0

        current_time = time.time()
        # 获取时间窗口内的变化次数
        recent_changes = [t for t in self.mouth_state_change_times
                          if current_time - t <= time_window]

        # 计算频率(每秒变化次数)
        frequency = len(recent_changes) / time_window
        return frequency

    def update_mouth_state_history(self, is_open, current_time):
        """更新嘴巴状态历史"""
        if not hasattr(self, 'mouth_open_history'):
            self.mouth_open_history = []

        state = "open" if is_open else "closed"
        self.mouth_open_history.append((current_time, state))

        # 只保留最近10秒的历史
        self.mouth_open_history = [(t, s) for t, s in self.mouth_open_history
                                   if current_time - t <= 10.0]

    def update_mouth_status(self, mouth_open, mouth_closed, current_time):
        """
        更新嘴巴状态，使用D-S理论辅助区分打哈欠和说话
        """
        # 如果不检测嘴巴，直接返回
        if not self.detect_mouth:
            return

        # 初始化必要的属性
        if not hasattr(self, 'mouth_state'):
            self.mouth_state = "闭合"
        if not hasattr(self, 'mouth_open_time'):
            self.mouth_open_time = current_time
        if not hasattr(self, 'mouth_movement_count'):
            self.mouth_movement_count = 0
        if not hasattr(self, 'yawn_detected'):
            self.yawn_detected = False
        if not hasattr(self, 'mouth_status_text'):
            self.mouth_status_text = ""
        if not hasattr(self, 'mouth_state_change_times'):
            self.mouth_state_change_times = []  # 记录嘴巴状态变化的时间点
        if not hasattr(self, 'mouth_open_max_conf'):
            self.mouth_open_max_conf = 0.0  # 记录当前嘴巴张开的最大置信度
        if not hasattr(self, 'speech_evidence'):
            self.speech_evidence = []  # 用于存储说话的证据
        if not hasattr(self, 'yawn_evidence'):
            self.yawn_evidence = []  # 用于存储打哈欠的证据
        if not hasattr(self, 'last_yawn_time'):
            self.last_yawn_time = 0
        if not hasattr(self, 'compound_fatigue_events'):
            self.compound_fatigue_events = []

        # 更新最大置信度
        if mouth_open and self.mouth_open_confidence > self.mouth_open_max_conf:
            self.mouth_open_max_conf = self.mouth_open_confidence

        # 检测嘴巴状态变化
        current_state = "张开" if mouth_open and not mouth_closed else "闭合"
        if not hasattr(self, 'prev_mouth_state'):
            self.prev_mouth_state = "闭合"

        if current_state != self.prev_mouth_state:
            self.mouth_state_change_times.append(current_time)
            # 只保留最近10次变化
            if len(self.mouth_state_change_times) > 10:
                self.mouth_state_change_times.pop(0)
            self.prev_mouth_state = current_state

        # 确定当前嘴巴是否张开 - 这里不需要再次判断，直接使用传入的参数
        is_mouth_open = mouth_open and not mouth_closed

        # 调用update_mouth_state_history来记录嘴巴状态历史
        self.update_mouth_state_history(is_mouth_open, current_time)

        if is_mouth_open:  # 使用优化后的is_mouth_open变量
            if self.mouth_state == "闭合":
                # 嘴巴从闭合变为张开，记录开始时间
                self.mouth_open_time = current_time
                self.mouth_movement_count = 1  # 初始化嘴巴动作计数
                self.mouth_open_max_conf = self.mouth_open_confidence  # 初始化最大置信度
                # 重置证据
                self.speech_evidence = []
                self.yawn_evidence = []

            self.mouth_state = "张开"

            # 计算持续时间
            mouth_open_duration = current_time - self.mouth_open_time

            # 计算嘴巴状态变化频率 (每秒变化次数)
            change_frequency = self.calculate_mouth_change_frequency(3.0)

            # 收集说话和打哈欠的证据
            # 证据1: 持续时间
            if mouth_open_duration < 1.0:
                self.speech_evidence.append(0.7)  # 短时间更可能是说话
                self.yawn_evidence.append(0.1)
            elif 1.0 <= mouth_open_duration <= 3.0:
                self.speech_evidence.append(0.3)
                self.yawn_evidence.append(0.8)  # 1-3秒更可能是打哈欠
            else:
                self.speech_evidence.append(0.5)
                self.yawn_evidence.append(0.5)  # 时间太长，不确定

            # 证据2: 变化频率
            if change_frequency > 1.5:
                self.speech_evidence.append(0.9)  # 高频变化，很可能是说话
                self.yawn_evidence.append(0.1)
            elif change_frequency < 0.5:
                self.speech_evidence.append(0.2)
                self.yawn_evidence.append(0.8)  # 低频变化，可能是打哈欠
            else:
                self.speech_evidence.append(0.5)
                self.yawn_evidence.append(0.5)

            # 证据3: 嘴巴张开程度
            if self.mouth_open_max_conf > 0.9:
                self.speech_evidence.append(0.2)
                self.yawn_evidence.append(0.95)  # 大张开，可能是打哈欠
            elif self.mouth_open_max_conf > 0.7:
                self.speech_evidence.append(0.4)
                self.yawn_evidence.append(0.8)
            else:
                self.speech_evidence.append(0.7)  # 小张开，可能是说话
                self.yawn_evidence.append(0.3)

            # 证据4: 眼睛状态（如果同时检测眼睛）
            if self.detect_eyes and hasattr(self, 'eye_state'):
                if self.eye_state == "闭合":
                    self.speech_evidence.append(0.1)
                    self.yawn_evidence.append(0.9)  # 眼睛闭合时打哈欠的可能性增加
                else:
                    self.speech_evidence.append(0.6)
                    self.yawn_evidence.append(0.4)

            # 使用D-S理论融合证据
            if len(self.speech_evidence) >= 2 and len(self.yawn_evidence) >= 2:
                # 计算平均证据值
                avg_speech = sum(self.speech_evidence) / len(self.speech_evidence)
                avg_yawn = sum(self.yawn_evidence) / len(self.yawn_evidence)

                # 构建基本概率分配
                speech_bpa = {'说话': avg_speech, '打哈欠': 0.0, '不确定': 1.0 - avg_speech}
                yawn_bpa = {'说话': 0.0, '打哈欠': avg_yawn, '不确定': 1.0 - avg_yawn}

                # 组合证据
                combined_bpa = ds_combine(speech_bpa, yawn_bpa)

                # 判断结果
                is_speaking = combined_bpa.get('说话', 0) > combined_bpa.get('打哈欠', 0)

                # 使用时序模式和变化频率增强判断
                temporal_score = self.analyze_mouth_temporal_pattern()
                is_yawning = ((combined_bpa.get('打哈欠', 0) > 0.5 and mouth_open_duration > 1.0) or \
                              (self.mouth_open_max_conf > 0.9 and mouth_open_duration > 1.5)) and \
                             (temporal_score > 0.6 or change_frequency < 0.5)

                # 更新状态
                if is_yawning and not self.yawn_detected:
                    self.yawn_count += 1
                    self.yawn_detected = True
                    self.yawn_times.append(current_time)
                    self.mouth_status_text = "打哈欠"

                    # 检测频繁打哈欠(两次打哈欠间隔小于30秒)
                    if (current_time - self.last_yawn_time) < 30.0 and self.last_yawn_time > 0:
                        self.compound_fatigue_events.append(("频繁打哈欠", current_time))

                    self.last_yawn_time = current_time

                elif is_speaking and mouth_open_duration > 0.5:
                    self.mouth_status_text = "正在说话"
                else:
                    self.mouth_status_text = "嘴巴张开"

        # 在嘴巴闭合处理部分
        elif mouth_closed and not mouth_open:
            if self.mouth_state == "张开":
                # 嘴巴从张开变为闭合，增加动作计数
                self.mouth_movement_count += 1

                # 如果之前检测到打哈欠，保持状态文本一段时间
                if self.mouth_status_text == "打哈欠":
                    # 可以设置一个计时器或延迟几帧再清除状态
                    if current_time - self.last_yawn_time > 2.0:  # 2秒后再清除
                        self.mouth_status_text = ""
                else:
                    self.mouth_status_text = ""

            self.mouth_state = "闭合"
            self.yawn_detected = False  # 重置打哈欠检测标志
            self.mouth_open_max_conf = 0.0  # 重置最大置信度

    def analyze_mouth_temporal_pattern(self):
        """分析嘴巴状态的时序模式，判断是打哈欠还是说话"""
        if not hasattr(self, 'mouth_open_history') or len(self.mouth_open_history) < 3:
            return 0.0

        current_time = time.time()
        # 获取最近5秒内的历史记录
        recent_history = [(t, s) for t, s in self.mouth_open_history if current_time - t <= 5.0]

        if len(recent_history) < 3:
            return 0.0

        # 计算状态变化频率
        changes = 0
        for i in range(1, len(recent_history)):
            if recent_history[i][1] != recent_history[i - 1][1]:
                changes += 1

        # 计算变化率
        change_rate = changes / len(recent_history)

        # 打哈欠通常变化少，说话变化频繁
        if change_rate < 0.2:  # 变化很少，可能是打哈欠
            return 0.9
        elif change_rate < 0.4:  # 变化较少
            return 0.7
        elif change_rate < 0.6:  # 变化中等
            return 0.4
        else:  # 变化频繁，可能是说话
            return 0.1

    def calculate_fatigue_indicators(self, current_time):
        """
        计算各项疲劳指标

        返回:
            indicators: 包含各项指标及其分数的字典
        """
        indicators = {}

        # 1. PERCLOS指标计算
        if self.detect_eyes and hasattr(self, 'perclos_value'):
            # PERCLOS阈值参考自研究
            if self.perclos_value < 0.15:
                perclos_score = 0.0  # 正常
            elif self.perclos_value < 0.25:
                perclos_score = (self.perclos_value - 0.15) / 0.1  # 0.0-1.0线性映射
            else:
                perclos_score = min(1.0, 0.5 + (self.perclos_value - 0.25) / 0.3)  # 0.5-1.0线性映射

            indicators['PERCLOS'] = {
                'value': self.perclos_value,
                'score': perclos_score,
                'weight': 0.35  # PERCLOS指标权重
            }

        # 2. 眨眼频率计算
        if self.detect_eyes and hasattr(self, 'blink_count') and hasattr(self, 'timestamps') and self.timestamps:
            # 计算每分钟眨眼次数
            detection_minutes = (current_time - self.detection_start_time) / 60
            if detection_minutes > 0:
                blinks_per_minute = self.blink_count / detection_minutes

                # 正常人每分钟眨眼15-20次，疲劳时会减少或增加
                if 12 <= blinks_per_minute <= 20:
                    blink_rate_score = 0.0  # 正常
                elif 8 <= blinks_per_minute < 12:
                    blink_rate_score = (12 - blinks_per_minute) / 4  # 0.0-1.0线性映射(减少)
                elif 20 < blinks_per_minute <= 30:
                    blink_rate_score = (blinks_per_minute - 20) / 10  # 0.0-1.0线性映射(增加)
                else:
                    blink_rate_score = 1.0  # 异常，可能是疲劳

                indicators['眨眼频率'] = {
                    'value': blinks_per_minute,
                    'score': blink_rate_score,
                    'weight': 0.15  # 眨眼频率指标权重
                }

        # 3. 长时间闭眼计算
        if self.detect_eyes and hasattr(self, 'current_eye_closed_duration'):
            if self.current_eye_closed_duration < 0.5:
                eye_closure_score = 0.0  # 正常
            elif self.current_eye_closed_duration < 1.0:
                eye_closure_score = (self.current_eye_closed_duration - 0.5) / 0.5  # 0.0-1.0线性映射
            else:
                eye_closure_score = min(1.0, 0.5 + (self.current_eye_closed_duration - 1.0) / 2.0)  # 0.5-1.0线性映射

            indicators['长时间闭眼'] = {
                'value': self.current_eye_closed_duration,
                'score': eye_closure_score,
                'weight': 0.25  # 长时间闭眼指标权重
            }

        # 4. 打哈欠频率计算
        if self.detect_mouth and hasattr(self, 'yawn_times'):
            # 计算最近3分钟内的打哈欠次数
            recent_yawns = [t for t in self.yawn_times if current_time - t < 180.0]
            recent_yawn_count = len(recent_yawns)

            if recent_yawn_count == 0:
                yawn_score = 0.0  # 正常
            elif recent_yawn_count == 1:
                yawn_score = 0.5  # 轻度疲劳
            elif recent_yawn_count == 2:
                yawn_score = 0.75  # 中度疲劳
            else:
                yawn_score = 1.0  # 严重疲劳

            indicators['打哈欠频率'] = {
                'value': recent_yawn_count,
                'score': yawn_score,
                'weight': 0.25  # 打哈欠频率指标权重
            }

        return indicators

    def update_fatigue_events(self, current_time):
        """更新和管理疲劳事件"""
        if not hasattr(self, 'compound_fatigue_events'):
            self.compound_fatigue_events = []

        # 清理旧事件（超过3分钟的事件）
        self.compound_fatigue_events = [(event, time) for event, time in self.compound_fatigue_events
                                        if current_time - time < 180.0]

        # 检测长时间闭眼事件
        if self.detect_eyes and hasattr(self, 'current_eye_closed_duration'):
            # 闭眼超过2秒视为长时间闭眼
            if self.current_eye_closed_duration > 2.0:
                # 检查是否已经记录了类似事件
                recent_long_eye_close = any(
                    "长时间闭眼" in event and current_time - time < 30.0
                    for event, time in self.compound_fatigue_events
                )

                if not recent_long_eye_close:
                    self.compound_fatigue_events.append(("长时间闭眼", current_time))

        # 检测频繁打哈欠事件
        if self.detect_mouth and hasattr(self, 'yawn_times'):
            # 计算最近2分钟内的打哈欠次数
            recent_yawns = [t for t in self.yawn_times if current_time - t < 120.0]

            # 2分钟内打哈欠2次以上视为频繁打哈欠
            if len(recent_yawns) >= 2:
                # 检查上一次记录频繁打哈欠事件的时间
                last_frequent_yawn_event = next(
                    (time for event, time in self.compound_fatigue_events if "频繁打哈欠" in event),
                    0
                )

                # 如果距离上次记录超过30秒，再次记录
                if current_time - last_frequent_yawn_event > 30.0:
                    self.compound_fatigue_events.append(("频繁打哈欠", current_time))

    def calculate_fatigue_score_by_ds_theory(self, current_time):
        """使用D-S证据理论计算疲劳分数和警报级别"""
        # 收集各项疲劳指标
        evidence_list = []

        # 1. PERCLOS指标
        if hasattr(self, 'perclos_value'):
            perclos_bpa = calculate_perclos_bpa(self.perclos_value)
            evidence_list.append(perclos_bpa)

        # 2. 闭眼持续时间指标
        if hasattr(self, 'current_eye_closed_duration'):
            eye_closure_bpa = calculate_eye_closure_bpa(self.current_eye_closed_duration)
            evidence_list.append(eye_closure_bpa)

        # 3. 眨眼频率指标
        if hasattr(self, 'blink_count') and hasattr(self, 'detection_start_time'):
            detection_minutes = (current_time - self.detection_start_time) / 60
            if detection_minutes > 0:
                blinks_per_minute = self.blink_count / detection_minutes
                blink_rate_bpa = calculate_blink_rate_bpa(blinks_per_minute)
                evidence_list.append(blink_rate_bpa)

        # 4. 打哈欠指标
        if hasattr(self, 'yawn_times'):
            # 计算最近3分钟内的打哈欠次数
            recent_yawns = [t for t in self.yawn_times if current_time - t < 180.0]
            recent_yawn_count = len(recent_yawns)
            yawn_bpa = calculate_yawn_bpa(recent_yawn_count)
            evidence_list.append(yawn_bpa)

        # 5. 头部姿态指标 (新增)
        if hasattr(self, 'head_down_duration') and hasattr(self, 'nodding_detected'):
            head_pose_bpa = calculate_head_pose_bpa(self.head_down_duration, self.nodding_detected)
            evidence_list.append(head_pose_bpa)

        # 如果没有有效指标，返回默认值
        if not evidence_list:
            self.raw_fatigue_score = 0.0
            self.fatigue_score = 0.0
            return

        # 使用D-S理论组合所有证据
        combined_bpa = combine_all_evidence(evidence_list)

        # 计算疲劳分数
        # 疲劳分数 = 轻度疲劳的概率 * 0.5 + 严重疲劳的概率 * 1.0
        fatigue_score = combined_bpa.get('轻度疲劳', 0) * 0.5 + combined_bpa.get('严重疲劳', 0) * 1.0

        # 保存原始疲劳分数
        self.raw_fatigue_score = fatigue_score

        # 将当前原始分数加入历史记录
        self.fatigue_score_history.append((current_time, self.raw_fatigue_score))

        # 只保留时间窗口内的分数记录
        self.fatigue_score_history = [
            (t, s) for t, s in self.fatigue_score_history
            if current_time - t <= self.smoothing_window_size
        ]

        # 应用时间窗口平滑
        self._apply_time_window_smoothing(current_time)

        # 应用疲劳事件提升
        self._apply_fatigue_events_boost(current_time)

        # 应用滞后阈值
        candidate_level = self._apply_hysteresis_thresholds()

        # 应用连续帧判断
        self._apply_consecutive_frames(candidate_level)

        # 保存各指标的值用于显示
        self.current_indicators = {
            'PERCLOS': {'value': self.perclos_value if hasattr(self, 'perclos_value') else 0.0,
                        'bpa': perclos_bpa if 'perclos_bpa' in locals() else {},
                        'weight': 0.25},  # 降低权重为0.25
            '闭眼持续时间': {
                'value': self.current_eye_closed_duration if hasattr(self, 'current_eye_closed_duration') else 0.0,
                'bpa': eye_closure_bpa if 'eye_closure_bpa' in locals() else {},
                'weight': 0.20},  # 降低权重为0.20
            '眨眼频率': {'value': blinks_per_minute if 'blinks_per_minute' in locals() else 0.0,
                         'bpa': blink_rate_bpa if 'blink_rate_bpa' in locals() else {},
                         'weight': 0.15},  # 保持权重不变
            '打哈欠频率': {'value': recent_yawn_count if 'recent_yawn_count' in locals() else 0,
                           'bpa': yawn_bpa if 'yawn_bpa' in locals() else {},
                           'weight': 0.20},  # 降低权重为0.20
            '头部姿态': {'value': self.head_down_duration if hasattr(self, 'head_down_duration') else 0.0,
                         'bpa': head_pose_bpa if 'head_pose_bpa' in locals() else {},
                         'weight': 0.20}  # 新增指标权重为0.20
        }

        # 保存组合后的BPA
        self.combined_bpa = combined_bpa

    def calculate_fatigue_score_by_mode(self, current_time):
        """根据当前检测模式计算疲劳分数和警报级别（带三重稳定机制）"""
        # 计算当前帧的疲劳指标
        indicators = self.calculate_fatigue_indicators(current_time)

        # 如果没有有效指标，返回默认值但不更新历史分数
        if not indicators:
            return

        # 根据检测模式选择指标
        valid_indicators = {}

        if self.detect_eyes and not self.detect_mouth:
            # 仅检测眼睛模式
            for key in ['PERCLOS', '眨眼频率', '长时间闭眼']:
                if key in indicators:
                    valid_indicators[key] = indicators[key]
        elif self.detect_mouth and not self.detect_eyes:
            # 仅检测嘴巴模式
            if '打哈欠频率' in indicators:
                valid_indicators['打哈欠频率'] = indicators['打哈欠频率']
                valid_indicators['打哈欠频率']['weight'] = 1.0
        else:
            # 综合检测模式
            valid_indicators = indicators

        # 计算当前帧的加权平均疲劳分数（原始分数）
        total_score = 0.0
        total_weight = 0.0

        for key, data in valid_indicators.items():
            total_score += data['score'] * data['weight']
            total_weight += data['weight']

        if total_weight > 0:
            self.raw_fatigue_score = total_score / total_weight
        else:
            self.raw_fatigue_score = 0.0

        # 将当前原始分数加入历史记录
        self.fatigue_score_history.append((current_time, self.raw_fatigue_score))

        # 只保留时间窗口内的分数记录
        self.fatigue_score_history = [
            (t, s) for t, s in self.fatigue_score_history
            if current_time - t <= self.smoothing_window_size
        ]

        # 第一层稳定：应用时间窗口平滑
        self._apply_time_window_smoothing(current_time)

        # 第二层稳定：应用疲劳事件提升
        self._apply_fatigue_events_boost(current_time)

        # 第三层稳定：应用滞后阈值
        candidate_level = self._apply_hysteresis_thresholds()

        # 第四层稳定：应用连续帧判断
        self._apply_consecutive_frames(candidate_level)

        # 保存各指标的值用于显示
        self.current_indicators = valid_indicators

    def _apply_fatigue_events_boost(self, current_time):
        """根据检测到的疲劳事件提高疲劳分数"""
        if not hasattr(self, 'compound_fatigue_events') or not self.compound_fatigue_events:
            return

        # 只考虑最近2分钟内的事件
        recent_events = [(event, time) for event, time in self.compound_fatigue_events
                         if current_time - time < 120.0]

        if not recent_events:
            return

        # 计算事件提升
        boost = 0.0

        # 统计不同类型的事件
        long_eye_close_count = sum(1 for event, _ in recent_events if "闭眼" in event)
        frequent_yawn_count = sum(1 for event, _ in recent_events if "哈欠" in event)
        nodding_count = sum(1 for event, _ in recent_events if "点头" in event)  # 新增点头计数

        # 长时间闭眼事件的提升
        if long_eye_close_count > 0:
            # 第一次长闭眼提升0.15，之后每次增加0.1，最多0.35
            eye_boost = min(0.35, 0.15 + 0.1 * (long_eye_close_count - 1))
            boost = max(boost, eye_boost)

        # 频繁打哈欠事件的提升
        if frequent_yawn_count > 0:
            # 第一次频繁打哈欠提升0.2，之后每次增加0.15，最多0.5
            yawn_boost = min(0.5, 0.2 + 0.15 * (frequent_yawn_count - 1))
            boost = max(boost, yawn_boost)

        # 点头瞌睡事件的提升 (新增)
        if nodding_count > 0:
            # 点头是强烈的疲劳信号，第一次提升0.3，之后每次增加0.2，最多0.7
            nodding_boost = min(0.7, 0.3 + 0.2 * (nodding_count - 1))
            boost = max(boost, nodding_boost)

        # 如果同时有闭眼和打哈欠事件，额外增加0.1的提升
        if long_eye_close_count > 0 and frequent_yawn_count > 0:
            boost += 0.1

        # 如果同时有点头和其他事件，额外增加0.15的提升 (新增)
        if nodding_count > 0 and (long_eye_close_count > 0 or frequent_yawn_count > 0):
            boost += 0.15

        # 限制最大提升为0.8
        boost = min(0.8, boost)

        # 应用提升，但确保不超过1.0
        self.fatigue_score = min(1.0, self.fatigue_score + boost)

    # 添加时间窗口平滑方法
    def _apply_time_window_smoothing(self, current_time):
        """应用改进的时间窗口平滑处理"""
        if not self.fatigue_score_history:
            self.fatigue_score = 0.0
            return

        # 计算最近5秒和整个窗口的平均值
        recent_scores = []
        all_scores = []

        for t, score in self.fatigue_score_history:
            all_scores.append(score)
            if current_time - t <= 5.0:  # 最近5秒的数据
                recent_scores.append(score)

        # 如果没有最近数据，使用所有数据
        if not recent_scores:
            recent_scores = all_scores

        # 计算加权平均：最近数据占70%权重，整体数据占30%权重
        recent_avg = sum(recent_scores) / len(recent_scores) if recent_scores else 0
        all_avg = sum(all_scores) / len(all_scores) if all_scores else 0

        # 最终平滑分数
        self.fatigue_score = 0.7 * recent_avg + 0.3 * all_avg

        # 添加额外的稳定性检查：防止分数突变
        if hasattr(self, 'last_smoothed_score'):
            max_change = 0.15  # 每帧最大变化幅度
            change = self.fatigue_score - self.last_smoothed_score
            if abs(change) > max_change:
                # 限制变化幅度
                self.fatigue_score = self.last_smoothed_score + (max_change if change > 0 else -max_change)

        # 保存当前平滑分数供下一帧使用
        self.last_smoothed_score = self.fatigue_score

    def _apply_hysteresis_thresholds(self):
        """应用改进的滞后阈值，考虑疲劳事件数量"""
        # 获取最近疲劳事件数量
        recent_events_count = 0
        if hasattr(self, 'compound_fatigue_events'):
            current_time = time.time()
            recent_events_count = sum(1 for _, event_time in self.compound_fatigue_events
                                      if current_time - event_time < 120.0)

        # 根据事件数量调整阈值
        normal_to_mild = self.NORMAL_TO_MILD
        mild_to_severe = self.MILD_TO_SEVERE

        # 疲劳事件越多，进入更高级别的阈值越低
        if recent_events_count >= 3:
            normal_to_mild = max(0.25, normal_to_mild - 0.05)  # 更容易进入轻度疲劳
            mild_to_severe = max(0.45, mild_to_severe - 0.1)  # 更容易进入严重疲劳
        elif recent_events_count >= 1:
            normal_to_mild = max(0.28, normal_to_mild - 0.02)  # 略微更容易进入轻度疲劳
            mild_to_severe = max(0.52, mild_to_severe - 0.03)  # 略微更容易进入严重疲劳

        # 根据当前状态和分数决定候选状态
        if self.fatigue_alert_level == 0:  # 当前为正常状态
            if self.fatigue_score >= normal_to_mild:
                return 1  # 候选升级到轻度警告
            return 0  # 保持正常

        elif self.fatigue_alert_level == 1:  # 当前为轻度疲劳
            if self.fatigue_score < self.MILD_TO_NORMAL:
                return 0  # 候选降级到正常
            elif self.fatigue_score >= mild_to_severe:
                return 2  # 候选升级到严重警告
            return 1  # 保持轻度警告

        else:  # 当前为严重疲劳
            if self.fatigue_score < self.SEVERE_TO_MILD:
                return 1  # 候选降级到轻度警告
            return 2  # 保持严重警告

    # 添加连续帧判断方法
    def _apply_consecutive_frames(self, candidate_level):
        """应用连续帧判断"""
        # 如果候选状态与当前状态不同，增加计数器
        if candidate_level != self.fatigue_alert_level:
            if candidate_level == self.consecutive_level_candidate:
                self.consecutive_frame_count += 1
            else:
                # 重置计数器，更新候选状态
                self.consecutive_level_candidate = candidate_level
                self.consecutive_frame_count = 1

            # 只有连续多帧都满足条件时才改变状态
            if self.consecutive_frame_count >= self.FRAMES_TO_CHANGE:
                self.fatigue_alert_level = candidate_level
                self.consecutive_frame_count = 0
        else:
            # 目标状态与当前状态相同，重置计数器
            self.consecutive_frame_count = 0

    def get_optimized_parameters(self):
        """根据当前检测模式获取优化的参数设置"""
        if self.detect_eyes and not self.detect_mouth:
            # 仅眼睛检测模式
            return {
                'NORMAL_TO_MILD': 0.35,
                'MILD_TO_NORMAL': 0.25,
                'MILD_TO_SEVERE': 0.65,
                'SEVERE_TO_MILD': 0.55,
                'FRAMES_TO_CHANGE': 5
            }
        elif self.detect_mouth and not self.detect_eyes:
            # 仅嘴巴检测模式
            return {
                'NORMAL_TO_MILD': 0.40,  # 提高阈值，减少误报
                'MILD_TO_NORMAL': 0.30,  # 提高阈值，增加稳定性
                'MILD_TO_SEVERE': 0.70,  # 提高阈值，减少误报
                'SEVERE_TO_MILD': 0.60,  # 提高阈值，增加稳定性
                'FRAMES_TO_CHANGE': 10  # 增加帧数，提高稳定性
            }
        else:
            # 综合检测模式
            return {
                'NORMAL_TO_MILD': 0.35,
                'MILD_TO_NORMAL': 0.25,
                'MILD_TO_SEVERE': 0.65,
                'SEVERE_TO_MILD': 0.55,
                'FRAMES_TO_CHANGE': 5
            }

    def reset_state(self):
        """完全重置所有状态变量"""
        # 重置基本疲劳变量
        self.fatigue_score = 0.0
        self.raw_fatigue_score = 0.0
        self.fatigue_alert_level = 0

        # 重置时间窗口平滑相关变量
        self.fatigue_score_history = []

        # 重置滞后阈值相关变量
        self.consecutive_level_candidate = 0
        self.consecutive_frame_count = 0

        # 重置其他疲劳相关变量
        self.perclos_value = 0.0
        self.eye_closed_frames = 0
        self.total_frames = 0
        self.current_eye_closed_duration = 0.0
        self.max_eye_closed_duration = 0.0
        self.compound_fatigue_events = []

        # 重置眼睛和嘴巴状态
        self.eye_state = "睁开"
        self.mouth_state = "闭合"
        self.eye_status = 1.0
        self.mouth_status = 1.0

        # 重置点头相关变量
        self.nodding_detected = False
        self.nodding_count = 0
        self.last_nodding_time = 0
        self.pitch_history = []
        self.nod_counter = 0
        self.head_down_duration = 0.0
        self.head_down_start_time = 0

        # 重置计数器
        self.blink_count = 0
        self.yawn_count = 0
        self.yawn_times = []

        # 重置检测开始时间
        self.detection_start_time = time.time()

        # 重置D-S理论相关变量
        if hasattr(self, 'speech_evidence'):
            self.speech_evidence = []
        if hasattr(self, 'yawn_evidence'):
            self.yawn_evidence = []
        if hasattr(self, 'combined_bpa'):
            self.combined_bpa = {}

        print("疲劳检测器状态已完全重置")

    # 添加调试信息输出方法（可选）
    def _debug_stability_info(self):
        """输出稳定性机制的调试信息"""
        print(f"原始分数: {self.raw_fatigue_score:.3f}, 平滑后分数: {self.fatigue_score:.3f}")
        print(f"当前状态: {self.fatigue_alert_level}, 候选状态: {self.consecutive_level_candidate}")
        print(f"连续帧计数: {self.consecutive_frame_count}/{self.FRAMES_TO_CHANGE}")
        print(f"历史记录数: {len(self.fatigue_score_history)}")
        print("-" * 50)

    def detect_motion(self, current_frame, prev_frame):
        """检测两帧之间的运动"""
        if current_frame is None or prev_frame is None:
            return False, 0.0

        try:
            # 转换为灰度图
            gray_current = cv2.cvtColor(current_frame, cv2.COLOR_BGR2GRAY)
            gray_prev = cv2.cvtColor(prev_frame, cv2.COLOR_BGR2GRAY)

            # 计算帧差异
            frame_diff = cv2.absdiff(gray_current, gray_prev)

            # 计算平均差异并与阈值比较
            motion_level = np.mean(frame_diff) / 255.0
            motion_detected = motion_level > self.motion_threshold

            return motion_detected, motion_level
        except Exception as e:
            print(f"运动检测出错: {str(e)}")
            return False, 0.0

    def should_process_frame(self):
        """决定是否处理当前帧"""
        # 基本跳帧逻辑
        self.frame_counter += 1
        if self.frame_counter % self.detection_interval != 0:
            return False

        # 如果检测到重要事件，临时减少跳帧
        if hasattr(self, 'fatigue_alert_level') and self.fatigue_alert_level > 0:
            # 疲劳状态下减少跳帧，提高检测频率
            return self.frame_counter % max(1, self.detection_interval - 1) == 0

        # 如果检测到运动，临时减少跳帧
        if hasattr(self, 'motion_detected') and self.motion_detected:
            return self.frame_counter % max(1, self.detection_interval - 1) == 0

        # 如果头部姿态异常，临时减少跳帧
        if hasattr(self, 'head_direction') and self.head_direction != "正向":
            return self.frame_counter % max(1, self.detection_interval - 1) == 0

        return True

    def process_frame(self, frame):
        """处理单帧图像"""
        # 处理开始前清理不必要的缓存
        if hasattr(self, '_frame_times') and len(self._frame_times) > 30:
            self._frame_times = self._frame_times[-30:]

        # 限制历史数据大小
        if hasattr(self, 'fatigue_score_history') and len(self.fatigue_score_history) > 300:
            self.fatigue_score_history = self.fatigue_score_history[-300:]

        if hasattr(self, 'pitch_history') and len(self.pitch_history) > 60:
            self.pitch_history = self.pitch_history[-60:]

        if hasattr(self, 'eye_state_history') and len(self.eye_state_history) > 30:
            self.eye_state_history = self.eye_state_history[-30:]

        # 每1000帧执行一次全面清理
        if hasattr(self, 'frame_count') and self.frame_count % 1000 == 0:
            import gc
            gc.collect()
            if torch.cuda.is_available():
                torch.cuda.empty_cache()

        # 定期调用数据限制方法
        if hasattr(self, 'frame_count') and self.frame_count % 100 == 0:
            self._limit_data_size()
            if torch.cuda.is_available():
                torch.cuda.empty_cache()

        # 记录处理开始时间
        process_start_time = time.time()

        # 保存原始帧以备回退
        original_frame = frame.copy()

        try:
            self.frame_count += 1

            # 性能优化: 如果设置了跳帧处理
            if hasattr(self, 'detection_interval') and hasattr(self, 'frame_counter'):
                # 使用智能跳帧策略
                if not self.should_process_frame():
                    # 如果不是需要处理的帧，直接返回上一次的结果
                    if hasattr(self, 'last_detection_results') and self.last_detection_results is not None:
                        return self.last_detection_results.copy()

            # 性能优化: 调整处理尺寸
            need_resize_back = False
            original_size = None
            if hasattr(self, 'process_size') and self.process_size:
                original_size = frame.shape[:2]
                frame = cv2.resize(frame, self.process_size)
                need_resize_back = True

            # 添加头部姿态检测
            landmarks = self.detect_head_pose(frame)

            # 如果检测到人脸关键点，保存供其他函数使用
            if landmarks is not None:
                self.face_landmarks = landmarks

            # 摄像头模式下的运动检测优化
            if hasattr(self, 'is_camera_mode') and self.is_camera_mode and hasattr(self,
                                                                                   'enable_motion_detection') and self.enable_motion_detection:
                # 每N帧进行一次运动检测
                if self.frame_count % self.motion_detection_interval == 0:
                    # 如果有上一帧，进行运动检测
                    if hasattr(self, 'prev_frame') and self.prev_frame is not None:
                        self.motion_detected, motion_level = self.detect_motion(frame, self.prev_frame)

                        # 如果没有检测到明显运动，可以跳过复杂处理
                        if not self.motion_detected and hasattr(self, 'last_detection_results'):
                            # 在结果上显示运动状态
                            if hasattr(self, 'last_detection_results'):
                                result_frame = self.last_detection_results.copy()
                                cv2.putText(result_frame, f"静止状态 ({motion_level:.3f})", (10, 30),
                                            cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)
                                return result_frame

                    # 保存当前帧用于下次比较
                    self.prev_frame = frame.copy()

            # 初始化状态变量
            left_eye_open = False
            left_eye_closed = False
            right_eye_open = False
            right_eye_closed = False
            mouth_open = False
            mouth_closed = False

            # 初始化置信度变量
            self.left_eye_open_confidence = 0.0
            self.left_eye_closed_confidence = 0.0
            self.right_eye_open_confidence = 0.0
            self.right_eye_closed_confidence = 0.0
            self.mouth_open_confidence = 0.0
            self.mouth_closed_confidence = 0.0

            # 清空上一次的检测结果
            self.last_detections = []

            # 更新总帧数
            self.total_frames += 1

            # 使用YOLO模型进行预测 - 优化后的YOLO推理
            if hasattr(self, 'yolo_conf') and hasattr(self, 'yolo_iou'):
                # 使用优化后的参数
                results = self.yolo_model.predict(
                    source=frame,
                    conf=self.yolo_conf,
                    iou=self.yolo_iou,
                    device=self.device,
                    verbose=False,
                    imgsz=frame.shape[:2]  # 直接使用当前帧尺寸，避免额外的调整
                )
            else:
                # 使用默认参数
                results = self.yolo_model.predict(
                    source=frame,
                    conf=0.25,
                    iou=0.5,
                    device=self.device,
                    verbose=False,
                    imgsz=frame.shape[:2]  # 直接使用当前帧尺寸，避免额外的调整
                )

            # 处理检测结果
            if len(results) > 0:
                boxes = results[0].boxes

                # 过滤异常检测框
                if hasattr(self, 'filter_abnormal_detections'):
                    boxes = self.filter_abnormal_detections(boxes, frame)

                if len(boxes) > 0:
                    for box in boxes:
                        # 获取边界框坐标
                        x1, y1, x2, y2 = map(int, box.xyxy[0].cpu().numpy())

                        # 获取类别
                        cls = int(box.cls[0].cpu().numpy())
                        conf = float(box.conf[0].cpu().numpy())

                        # 定义类别名称和颜色
                        class_names = ['eyes_open', 'eyes_close', 'mouth_open', 'mouth_close']
                        colors = [(0, 255, 0), (0, 0, 255), (255, 0, 0), (255, 255, 0)]

                        # 确保类别索引在有效范围内
                        if 0 <= cls < len(class_names):
                            label = class_names[cls]
                            color = colors[cls]

                            # 根据检测选项过滤
                            if cls in [0, 1]:  # 眼睛相关类别
                                if self.detect_eyes:
                                    # 保存检测结果，使用类别ID而非标签
                                    self.last_detections.append((cls, conf, x1, y1, x2, y2))
                                    # 绘制边界框
                                    cv2.rectangle(frame, (x1, y1), (x2, y2), color, 2)

                                    # 根据类别更新状态
                                    if cls == 0:  # 眼睛睁开
                                        # 判断是左眼还是右眼
                                        center_x = (x1 + x2) / 2
                                        if center_x < frame.shape[1] / 2:  # 在图像左半部分
                                            right_eye_open = True
                                            self.right_eye_open_confidence = conf
                                        else:  # 在图像右半部分
                                            left_eye_open = True
                                            self.left_eye_open_confidence = conf
                                    elif cls == 1:  # 眼睛闭合
                                        # 判断是左眼还是右眼
                                        center_x = (x1 + x2) / 2
                                        if center_x < frame.shape[1] / 2:  # 在图像左半部分
                                            right_eye_closed = True
                                            self.right_eye_closed_confidence = conf
                                        else:  # 在图像右半部分
                                            left_eye_closed = True
                                            self.left_eye_closed_confidence = conf

                            elif cls in [2, 3]:  # 嘴巴相关类别
                                if self.detect_mouth:
                                    # 保存检测结果，使用类别ID而非标签
                                    self.last_detections.append((cls, conf, x1, y1, x2, y2))
                                    # 绘制边界框
                                    cv2.rectangle(frame, (x1, y1), (x2, y2), color, 2)

                                    # 根据类别更新状态
                                    if cls == 2:  # 嘴巴张开
                                        mouth_open = True
                                        self.mouth_open_confidence = conf
                                    elif cls == 3:  # 嘴巴闭合
                                        mouth_closed = True
                                        self.mouth_closed_confidence = conf

            # 更新眼睛和嘴巴状态
            current_time = time.time()

            # 更新左右眼状态
            if left_eye_open and not left_eye_closed:
                self.left_eye_state = "睁开"
                self.left_eye_open_time = current_time
            elif left_eye_closed and not left_eye_open:
                self.left_eye_state = "闭合"
                self.left_eye_closed_time = current_time

            if right_eye_open and not right_eye_closed:
                self.right_eye_state = "睁开"
                self.right_eye_open_time = current_time
            elif right_eye_closed and not right_eye_open:
                self.right_eye_state = "闭合"
                self.right_eye_closed_time = current_time

            # 记录当前眼睛状态到历史
            if hasattr(self, 'eye_state') and hasattr(self, 'eye_state_history'):
                self.eye_state_history.append(self.eye_state)
                if len(self.eye_state_history) > self.eye_state_history_size:
                    self.eye_state_history.pop(0)

            # 眼睛状态逻辑
            if self.detect_eyes:
                # 合并左右眼状态
                eyes_open = left_eye_open or right_eye_open
                eyes_closed = left_eye_closed or right_eye_closed

                if eyes_open and not eyes_closed:
                    if self.eye_state == "闭合":
                        # 眼睛从闭合变为睁开，记录眨眼持续时间
                        blink_duration = current_time - self.eye_closed_time
                        if 0.1 <= blink_duration <= 0.5:  # 正常眨眼时间范围
                            self.blink_count += 1
                            self.blink_durations.append(blink_duration)
                            # 保留最近10次眨眼记录
                            if len(self.blink_durations) > 10:
                                self.blink_durations.pop(0)

                    self.eye_state = "睁开"
                    self.eye_open_time = current_time
                    self.eye_status = max(self.left_eye_open_confidence, self.right_eye_open_confidence)

                    # 重置当前闭眼持续时间
                    self.current_eye_closed_duration = 0.0

                elif eyes_closed and not eyes_open:
                    if self.eye_state == "睁开":
                        # 眼睛从睁开变为闭合，记录闭眼开始时间
                        self.eye_closed_time = current_time

                    self.eye_state = "闭合"

                    # 计算闭眼持续时间
                    eye_closed_duration = current_time - self.eye_closed_time
                    self.current_eye_closed_duration = eye_closed_duration

                    # 更新最大闭眼持续时间
                    if self.current_eye_closed_duration > self.max_eye_closed_duration:
                        self.max_eye_closed_duration = self.current_eye_closed_duration

                    # 检测长时间闭眼(超过1.5秒)
                    if self.current_eye_closed_duration > 1.5 and (current_time - self.last_long_blink_time) > 5.0:
                        self.last_long_blink_time = current_time
                        self.compound_fatigue_events.append(("长时间闭眼", current_time))

                    # 更新PERCLOS计算
                    self.eye_closed_frames += 1

            # 嘴巴状态逻辑
            if self.detect_mouth:
                # 更新嘴巴状态
                self.update_mouth_status(mouth_open, mouth_closed, current_time)

            # 计算PERCLOS
            if self.total_frames > 0:
                # 限制计算窗口大小
                window_size = min(self.total_frames, self.perclos_window)
                if window_size > 0:
                    self.perclos_value = min(1.0, self.eye_closed_frames / window_size)

                # 如果超过窗口大小，重置计数
                if self.total_frames % self.perclos_window == 0:
                    self.eye_closed_frames = 0

            # 更新疲劳事件
            self.update_fatigue_events(current_time)

            # 计算疲劳分数
            self.calculate_fatigue_score_by_ds_theory(current_time)

            # 确定疲劳等级
            if self.fatigue_score < 0.3:
                self.fatigue_level = "正常"
                fatigue_color = (0, 255, 0)  # 绿色
            elif self.fatigue_score < 0.6:
                self.fatigue_level = "轻度疲劳"
                fatigue_color = (0, 165, 255)  # 橙色
            else:
                self.fatigue_level = "严重疲劳"
                fatigue_color = (0, 0, 255)  # 红色

            # 记录数据用于生成图表
            if hasattr(self, 'video_fps') and self.video_fps and self.video_fps > 0:
                # 每秒记录一次数据（使用实际帧率）
                if self.frame_count % int(self.video_fps) == 0:
                    elapsed_seconds = self.frame_count / self.video_fps
                    self.timestamps.append(elapsed_seconds)
                    self.fatigue_scores_history.append(self.fatigue_score)
                    self.perclos_history.append(self.perclos_value)

                    # 无论眼睛状态如何，都记录当前闭眼持续时间
                    if hasattr(self, 'current_eye_closed_duration'):
                        self.eye_closed_duration_history.append(self.current_eye_closed_duration)
                    else:
                        self.eye_closed_duration_history.append(0.0)

                    self.alert_level_history.append(self.fatigue_alert_level)
            else:
                # 使用默认帧率30fps
                if self.frame_count % 30 == 0:
                    self.timestamps.append(self.frame_count / 30)
                    self.fatigue_scores_history.append(self.fatigue_score)
                    self.perclos_history.append(self.perclos_value)

                    # 无论眼睛状态如何，都记录当前闭眼持续时间
                    if hasattr(self, 'current_eye_closed_duration'):
                        self.eye_closed_duration_history.append(self.current_eye_closed_duration)
                    else:
                        self.eye_closed_duration_history.append(0.0)

                    self.alert_level_history.append(self.fatigue_alert_level)

            # 性能优化: 如果需要调整回原始大小
            if need_resize_back and original_size:
                frame = cv2.resize(frame, (original_size[1], original_size[0]))

            # 保存当前处理结果
            self.last_detection_results = frame.copy()

            # 记录处理时间
            process_end_time = time.time()
            process_time = process_end_time - process_start_time

            if not hasattr(self, '_frame_times'):
                self._frame_times = []

            self._frame_times.append(process_time)
            # 只保留最近30次处理时间
            if len(self._frame_times) > 30:
                self._frame_times.pop(0)

            return frame

        except Exception as e:
            print(f"处理帧时出错: {str(e)}")
            import traceback
            traceback.print_exc()

            return original_frame

    def process_frame_async(self, frame, callback=None):
        """
        异步处理帧，避免阻塞UI线程

        参数:
            frame: 输入帧
            callback: 处理完成后的回调函数
        """

        def process_thread():
            try:
                result = self.process_frame(frame)
                if callback:
                    callback(result)
            except Exception as e:
                print(f"异步处理帧时出错: {str(e)}")
                import traceback
                traceback.print_exc()

        # 创建并启动处理线程
        thread = threading.Thread(target=process_thread)
        thread.daemon = True
        thread.start()

    def optimize_yolo_inference(self):
        """优化YOLO推理而不牺牲精度"""
        # 1. 确保模型在正确的设备上
        self.yolo_model.to(self.device)

        # 2. 预热模型
        print("正在预热YOLO模型...")
        dummy_input = torch.zeros((1, 3, 384, 384), device=self.device)
        for _ in range(3):
            with torch.no_grad():
                _ = self.yolo_model(dummy_input)

        # 3. 设置推理参数
        self.yolo_conf = 0.25  # 置信度阈值
        self.yolo_iou = 0.5  # 保持原有IOU阈值
        self.yolo_imgsz = (384, 384)  # 固定输入尺寸，确保是32的倍数

        # 4. 优化批处理
        self.batch_size = 1  # 默认单帧处理
        if hasattr(self, 'is_camera_mode') and not self.is_camera_mode:
            self.batch_size = 4  # 批处理多帧

        # 5. 设置智能跳帧
        self.base_interval = 2  # 基础跳帧间隔
        self.adaptive_interval = True  # 启用自适应跳帧

        print("YOLO推理优化完成")

    def apply_eye_state_smoothing(self):
        """应用眼睛状态平滑处理"""
        # 使用最近的眼睛状态历史
        if hasattr(self, 'eye_state_history') and self.eye_state_history:
            # 计算最近5帧中眼睛睁开和闭合的比例
            recent_history = self.eye_state_history[-5:] if len(self.eye_state_history) >= 5 else self.eye_state_history
            open_count = recent_history.count("睁开")
            closed_count = recent_history.count("闭合")

            # 根据历史状态的多数决定当前状态
            if open_count > closed_count:
                self.eye_state = "睁开"
                # 降低置信度以反映这是推断而非直接检测
                self.eye_status = max(0.6, self.eye_status * 0.9) if hasattr(self, 'eye_status') else 0.6
                self.left_eye_open_confidence = self.eye_status
                self.right_eye_open_confidence = self.eye_status
            else:
                self.eye_state = "闭合"
                self.eye_status = max(0.6, self.eye_status * 0.9) if hasattr(self, 'eye_status') else 0.6
                self.left_eye_closed_confidence = self.eye_status
                self.right_eye_closed_confidence = self.eye_status

            # # 添加头部姿态权重
            # if hasattr(self, 'head_pose'):
            #     yaw_abs = abs(self.head_pose['yaw'])
            #     # 当头部偏转角度大时，增加平滑处理的权重
            #     if yaw_abs > 25:
            #         # 更保守地使用历史状态
            #         self.eye_status = max(0.7, self.eye_status)
            #         self.left_eye_open_confidence = self.eye_status if self.eye_state == "睁开" else 0.0
            #         self.right_eye_open_confidence = self.eye_status if self.eye_state == "睁开" else 0.0
            #         self.left_eye_closed_confidence = self.eye_status if self.eye_state == "闭合" else 0.0
            #         self.right_eye_closed_confidence = self.eye_status if self.eye_state == "闭合" else 0.0

    # 在 FatigueDetector 类中添加这个新方法
    def filter_abnormal_detections(self, boxes, frame):
        """过滤异常的检测框"""
        filtered_boxes = []

        for box in boxes:
            # 获取边界框坐标
            x1, y1, x2, y2 = map(int, box.xyxy[0].cpu().numpy())

            # 获取类别
            cls = int(box.cls[0].cpu().numpy())
            conf = float(box.conf[0].cpu().numpy())

            # 计算框的宽高比
            width = x2 - x1
            height = y2 - y1
            aspect_ratio = width / height if height > 0 else 0

            # 过滤异常检测框
            is_valid = True

            # 眼睛检测框通常是矩形的，宽高比在0.5-3.0之间
            if cls in [0, 1]:  # 眼睛类别
                if aspect_ratio < 0.3 or aspect_ratio > 4.0:
                    is_valid = False

                # 眼睛大小不应该超过图像宽度的1/3
                if width > frame.shape[1] / 3:
                    is_valid = False

            # 嘴巴检测框通常是矩形的，宽高比在1.0-4.0之间
            elif cls in [2, 3]:  # 嘴巴类别
                if aspect_ratio < 0.5 or aspect_ratio > 5.0:
                    is_valid = False

                # 嘴巴大小不应该超过图像宽度的1/2
                if width > frame.shape[1] / 2:
                    is_valid = False

            if is_valid:
                filtered_boxes.append(box)

        return filtered_boxes



    def get_detection_info(self):
        """获取检测信息"""
        info = []
        if hasattr(self, 'last_detections'):
            for cls, conf, x1, y1, x2, y2 in self.last_detections:
                class_names = ['eyes_open', 'eyes_close', 'mouth_open', 'mouth_close']
                if 0 <= cls < len(class_names):
                    info.append(f"{class_names[cls]}: {conf:.2f} 位置:[{x1},{y1},{x2},{y2}]")
        return info

    def get_left_eye_state(self):
        """获取左眼状态"""
        if hasattr(self, 'left_eye_state'):
            return self.left_eye_state
        return "未知"

    def get_right_eye_state(self):
        """获取右眼状态"""
        if hasattr(self, 'right_eye_state'):
            return self.right_eye_state
        return "未知"

    def get_left_eye_confidence(self):
        """获取左眼状态置信度"""
        if self.left_eye_state == "睁开":
            return self.left_eye_open_confidence
        elif self.left_eye_state == "闭合":
            return self.left_eye_closed_confidence
        return 0.0

    def get_right_eye_confidence(self):
        """获取右眼状态置信度"""
        if self.right_eye_state == "睁开":
            return self.right_eye_open_confidence
        elif self.right_eye_state == "闭合":
            return self.right_eye_closed_confidence
        return 0.0

    def get_eye_state(self):
        """获取眼睛状态"""
        if hasattr(self, 'eye_state'):
            return self.eye_state
        return "未知"

    def get_mouth_state(self):
        """获取嘴巴状态"""
        if hasattr(self, 'mouth_state'):
            return self.mouth_state
        return "未知"

    def get_fatigue_level(self):
        """获取疲劳等级"""
        if hasattr(self, 'fatigue_level'):
            return self.fatigue_level
        return "未知"

    def get_fatigue_score(self):
        """获取疲劳分数"""
        if hasattr(self, 'fatigue_score'):
            return self.fatigue_score
        return 0.0

    def get_blink_count(self):
        """获取眨眼次数"""
        if hasattr(self, 'blink_count'):
            return self.blink_count
        return 0

    def get_yawn_count(self):
        """获取打哈欠次数"""
        if hasattr(self, 'yawn_count'):
            return self.yawn_count
        return 0

    def get_avg_blink_duration(self):
        """获取平均眨眼时长(毫秒)"""
        if hasattr(self, 'blink_durations') and len(self.blink_durations) > 0:
            return int(np.mean(self.blink_durations) * 1000)
        return 0

    def get_eye_confidence(self):
        """获取眼睛状态置信度"""
        if hasattr(self, 'eye_state'):
            if self.eye_state == "睁开":
                # 使用左右眼睁开置信度的最大值
                return max(self.left_eye_open_confidence, self.right_eye_open_confidence)
            elif self.eye_state == "闭合":
                # 使用左右眼闭合置信度的最大值
                return max(self.left_eye_closed_confidence, self.right_eye_closed_confidence)
        return 0.0

    def get_mouth_confidence(self):
        """获取嘴巴状态置信度"""
        if hasattr(self, 'mouth_state'):
            if self.mouth_state == "张开" and hasattr(self, 'mouth_open_confidence'):
                return self.mouth_open_confidence
            elif self.mouth_state == "闭合" and hasattr(self, 'mouth_closed_confidence'):
                return self.mouth_closed_confidence
        return 0.0

    def get_perclos(self):
        """获取PERCLOS值(眼睛闭合时间比例)"""
        if hasattr(self, 'perclos_value'):
            return self.perclos_value
        return 0.0

    def get_max_eye_closed_duration(self):
        """获取最大闭眼持续时间(秒)"""
        if hasattr(self, 'max_eye_closed_duration'):
            return self.max_eye_closed_duration
        return 0.0

    def get_compound_fatigue_events(self):
        """获取复合疲劳事件列表"""
        if hasattr(self, 'compound_fatigue_events'):
            # 只返回最近2分钟内的事件
            current_time = time.time()
            return [e for e in self.compound_fatigue_events
                    if current_time - e[1] < 120.0]
        return []

    def get_fatigue_alert_level(self):
        """获取疲劳警报级别(0-正常，1-轻度警告，2-严重警告)"""
        if hasattr(self, 'fatigue_alert_level'):
            return self.fatigue_alert_level
        return 0

    def get_current_indicators(self):
        """获取当前使用的疲劳指标"""
        if hasattr(self, 'current_indicators'):
            return self.current_indicators
        return {}

    def get_detection_mode(self):
        """获取当前检测模式"""
        if self.detect_eyes and self.detect_mouth:
            return "综合检测"
        elif self.detect_eyes:
            return "仅眼睛检测"
        elif self.detect_mouth:
            return "仅嘴巴检测"
        else:
            return "未检测"

    def _limit_data_size(self):
        """限制各种数据结构的大小，防止内存泄漏"""
        # 限制历史数据
        if hasattr(self, 'eye_state_history') and len(self.eye_state_history) > 500:
            self.eye_state_history = self.eye_state_history[-500:]

        if hasattr(self, 'mouth_state_history') and len(self.mouth_state_history) > 500:
            self.mouth_state_history = self.mouth_state_history[-500:]

        if hasattr(self, 'fatigue_scores') and len(self.fatigue_scores) > 500:
            self.fatigue_scores = self.fatigue_scores[-500:]

        if hasattr(self, 'blink_durations') and len(self.blink_durations) > 50:
            self.blink_durations = self.blink_durations[-50:]

        if hasattr(self, '_frame_times') and len(self._frame_times) > 100:
            self._frame_times = self._frame_times[-100:]

    def report_performance_metrics(self):
        """报告性能指标"""
        metrics = {
            'avg_process_time': np.mean(self._frame_times) if hasattr(self,
                                                                      '_frame_times') and self._frame_times else 0,
            'max_process_time': max(self._frame_times) if hasattr(self, '_frame_times') and self._frame_times else 0,
            'min_process_time': min(self._frame_times) if hasattr(self, '_frame_times') and self._frame_times else 0,
            'frame_buffer_size': self.frame_queue.qsize() if hasattr(self, 'frame_queue') else 0,
            'result_buffer_size': self.result_queue.qsize() if hasattr(self, 'result_queue') else 0,
            'detection_interval': self.detection_interval if hasattr(self, 'detection_interval') else 1,
            'process_size': self.process_size if hasattr(self, 'process_size') else None,
        }

        print("\n===== 性能指标报告 =====")
        print(f"平均处理时间: {metrics['avg_process_time'] * 1000:.1f} ms")
        print(f"最大处理时间: {metrics['max_process_time'] * 1000:.1f} ms")
        print(f"最小处理时间: {metrics['min_process_time'] * 1000:.1f} ms")
        print(f"帧缓冲区大小: {metrics['frame_buffer_size']}")
        print(f"结果缓冲区大小: {metrics['result_buffer_size']}")
        print(f"检测间隔: 每{metrics['detection_interval']}帧处理一次")
        print(f"处理分辨率: {metrics['process_size']}")
        print("========================\n")

        return metrics

    def set_detection_mode(self, detect_eyes=True, detect_mouth=True):
        """设置检测模式"""
        self.detect_eyes = detect_eyes
        self.detect_mouth = detect_mouth
        print(f"检测模式已设置为: {self.get_detection_mode()}")

        # 应用当前模式的优化参数
        params = self.get_optimized_parameters()
        self.NORMAL_TO_MILD = params['NORMAL_TO_MILD']
        self.MILD_TO_NORMAL = params['MILD_TO_NORMAL']
        self.MILD_TO_SEVERE = params['MILD_TO_SEVERE']
        self.SEVERE_TO_MILD = params['SEVERE_TO_MILD']
        self.FRAMES_TO_CHANGE = params['FRAMES_TO_CHANGE']

        print(f"已应用{self.get_detection_mode()}模式的优化参数")

    def get_avg_process_time(self):
        """获取平均处理时间(毫秒)"""
        if hasattr(self, '_frame_times') and self._frame_times:
            return int(np.mean(self._frame_times) * 1000)
        return 0

    def generate_report(self, output_folder):
        """生成疲劳检测报告和图表"""
        if not os.path.exists(output_folder):
            os.makedirs(output_folder)

        # 生成疲劳分数曲线图
        self._generate_fatigue_score_chart(output_folder)

        # 生成PERCLOS曲线图
        self._generate_perclos_chart(output_folder)

        # 生成闭眼持续时间曲线图
        self._generate_eye_closed_duration_chart(output_folder)

        # 生成警报级别曲线图
        self._generate_alert_level_chart(output_folder)

        # 生成综合报告图
        self._generate_combined_chart(output_folder)

        # 生成统计报告
        self._generate_statistics_report(output_folder)

        return True

    def _generate_fatigue_score_chart(self, output_folder):
        """生成疲劳分数曲线图"""
        if not self.timestamps or not self.fatigue_scores_history:
            return False

        plt.figure(figsize=(10, 6))
        plt.plot(self.timestamps, self.fatigue_scores_history, 'r-', linewidth=2)
        plt.axhline(y=0.3, color='g', linestyle='--', alpha=0.7)
        plt.axhline(y=0.6, color='r', linestyle='--', alpha=0.7)
        plt.fill_between(self.timestamps, 0, self.fatigue_scores_history, alpha=0.2, color='r')
        plt.title('驾驶员疲劳分数变化曲线', fontproperties='SimHei', fontsize=14)
        plt.xlabel('时间 (秒)', fontproperties='SimHei', fontsize=12)
        plt.ylabel('疲劳分数', fontproperties='SimHei', fontsize=12)
        plt.grid(True, alpha=0.3)
        plt.legend(['疲劳分数', '正常阈值', '严重阈值'], prop={'family': 'SimHei'})
        plt.tight_layout()
        plt.savefig(os.path.join(output_folder, 'fatigue_score.png'), dpi=100)
        plt.close()
        return True

    def _generate_perclos_chart(self, output_folder):
        """生成PERCLOS曲线图"""
        if not self.timestamps or not self.perclos_history:
            return False

        plt.figure(figsize=(10, 6))
        plt.plot(self.timestamps, self.perclos_history, 'b-', linewidth=2)
        plt.axhline(y=0.25, color='orange', linestyle='--', alpha=0.7)
        plt.axhline(y=0.4, color='r', linestyle='--', alpha=0.7)
        plt.fill_between(self.timestamps, 0, self.perclos_history, alpha=0.2, color='b')
        plt.title('PERCLOS值变化曲线', fontproperties='SimHei', fontsize=14)
        plt.xlabel('时间 (秒)', fontproperties='SimHei', fontsize=12)
        plt.ylabel('PERCLOS值', fontproperties='SimHei', fontsize=12)
        plt.grid(True, alpha=0.3)
        plt.legend(['PERCLOS', '轻度疲劳阈值', '严重疲劳阈值'], prop={'family': 'SimHei'})
        plt.tight_layout()
        plt.savefig(os.path.join(output_folder, 'perclos.png'), dpi=100)
        plt.close()
        return True

    def _generate_eye_closed_duration_chart(self, output_folder):
        """生成闭眼持续时间曲线图"""
        if not self.timestamps or not self.eye_closed_duration_history:
            return False

        plt.figure(figsize=(10, 6))
        plt.plot(self.timestamps, self.eye_closed_duration_history, 'g-', linewidth=2)
        plt.axhline(y=1.0, color='orange', linestyle='--', alpha=0.7)
        plt.axhline(y=2.0, color='r', linestyle='--', alpha=0.7)
        plt.fill_between(self.timestamps, 0, self.eye_closed_duration_history, alpha=0.2, color='g')
        plt.title('闭眼持续时间变化曲线', fontproperties='SimHei', fontsize=14)
        plt.xlabel('时间 (秒)', fontproperties='SimHei', fontsize=12)
        plt.ylabel('闭眼持续时间 (秒)', fontproperties='SimHei', fontsize=12)
        plt.grid(True, alpha=0.3)
        plt.legend(['闭眼持续时间', '轻度疲劳阈值', '严重疲劳阈值'], prop={'family': 'SimHei'})
        plt.tight_layout()
        plt.savefig(os.path.join(output_folder, 'eye_closed_duration.png'), dpi=100)
        plt.close()
        return True

    def _generate_alert_level_chart(self, output_folder):
        """生成警报级别曲线图"""
        if not self.timestamps or not self.alert_level_history:
            return False

        plt.figure(figsize=(10, 6))
        plt.step(self.timestamps, self.alert_level_history, 'r-', linewidth=2, where='post')
        plt.fill_between(self.timestamps, 0, self.alert_level_history, alpha=0.2, color='r', step='post')
        plt.title('疲劳警报级别变化曲线', fontproperties='SimHei', fontsize=14)
        plt.xlabel('时间 (秒)', fontproperties='SimHei', fontsize=12)
        plt.ylabel('警报级别', fontproperties='SimHei', fontsize=12)
        plt.yticks([0, 1, 2], ['正常', '轻度警告', '严重警告'], fontproperties='SimHei')
        plt.grid(True, alpha=0.3)
        plt.tight_layout()
        plt.savefig(os.path.join(output_folder, 'alert_level.png'), dpi=100)
        plt.close()
        return True

    def _generate_combined_chart(self, output_folder):
        """生成综合报告图"""
        if not self.timestamps:
            return False

        fig, axes = plt.subplots(4, 1, figsize=(12, 16), sharex=True)

        # 1. 疲劳分数
        if self.fatigue_scores_history:
            axes[0].plot(self.timestamps, self.fatigue_scores_history, 'r-', linewidth=2)
            axes[0].axhline(y=0.3, color='g', linestyle='--', alpha=0.7)
            axes[0].axhline(y=0.6, color='r', linestyle='--', alpha=0.7)
            axes[0].fill_between(self.timestamps, 0, self.fatigue_scores_history, alpha=0.2, color='r')
            axes[0].set_title('疲劳分数变化曲线', fontproperties='SimHei', fontsize=14)
            axes[0].set_ylabel('疲劳分数', fontproperties='SimHei', fontsize=12)
            axes[0].grid(True, alpha=0.3)
            axes[0].legend(['疲劳分数', '正常阈值', '严重阈值'], prop={'family': 'SimHei'})

        # 2. PERCLOS
        if self.perclos_history:
            axes[1].plot(self.timestamps, self.perclos_history, 'b-', linewidth=2)
            axes[1].axhline(y=0.25, color='orange', linestyle='--', alpha=0.7)
            axes[1].axhline(y=0.4, color='r', linestyle='--', alpha=0.7)
            axes[1].fill_between(self.timestamps, 0, self.perclos_history, alpha=0.2, color='b')
            axes[1].set_title('PERCLOS值变化曲线', fontproperties='SimHei', fontsize=14)
            axes[1].set_ylabel('PERCLOS值', fontproperties='SimHei', fontsize=12)
            axes[1].grid(True, alpha=0.3)
            axes[1].legend(['PERCLOS', '轻度疲劳阈值', '严重疲劳阈值'], prop={'family': 'SimHei'})

        # 3. 闭眼持续时间
        if self.eye_closed_duration_history:
            axes[2].plot(self.timestamps, self.eye_closed_duration_history, 'g-', linewidth=2)
            axes[2].axhline(y=1.0, color='orange', linestyle='--', alpha=0.7)
            axes[2].axhline(y=2.0, color='r', linestyle='--', alpha=0.7)
            axes[2].fill_between(self.timestamps, 0, self.eye_closed_duration_history, alpha=0.2, color='g')
            axes[2].set_title('闭眼持续时间变化曲线', fontproperties='SimHei', fontsize=14)
            axes[2].set_ylabel('闭眼持续时间 (秒)', fontproperties='SimHei', fontsize=12)
            axes[2].grid(True, alpha=0.3)
            axes[2].legend(['闭眼持续时间', '轻度疲劳阈值', '严重疲劳阈值'], prop={'family': 'SimHei'})

        # 4. 警报级别
        if self.alert_level_history:
            axes[3].step(self.timestamps, self.alert_level_history, 'r-', linewidth=2, where='post')
            axes[3].fill_between(self.timestamps, 0, self.alert_level_history, alpha=0.2, color='r', step='post')
            axes[3].set_title('疲劳警报级别变化曲线', fontproperties='SimHei', fontsize=14)
            axes[3].set_xlabel('时间 (秒)', fontproperties='SimHei', fontsize=12)
            axes[3].set_ylabel('警报级别', fontproperties='SimHei', fontsize=12)
            axes[3].set_yticks([0, 1, 2])
            axes[3].set_yticklabels(['正常', '轻度警告', '严重警告'], fontproperties='SimHei')
            axes[3].grid(True, alpha=0.3)

        plt.tight_layout()
        plt.savefig(os.path.join(output_folder, 'combined_report.png'), dpi=100)
        plt.close()
        return True

    def _generate_statistics_report(self, output_folder):
        """生成统计报告"""
        report_file = os.path.join(output_folder, 'statistics_report.txt')

        with open(report_file, 'w', encoding='utf-8') as f:
            f.write("疲劳驾驶检测统计报告\n")
            f.write("=" * 50 + "\n\n")

            # 检测时长
            detection_duration = 0
            if self.timestamps:
                detection_duration = max(self.timestamps)
            f.write(f"检测时长: {detection_duration:.2f} 秒 ({detection_duration / 60:.2f} 分钟)\n\n")

            # 眨眼统计
            f.write("眨眼统计:\n")
            f.write("-" * 30 + "\n")
            f.write(f"总眨眼次数: {self.get_blink_count()} 次\n")
            if detection_duration > 0:
                blinks_per_minute = self.get_blink_count() / (detection_duration / 60)
                f.write(f"平均每分钟眨眼次数: {blinks_per_minute:.2f} 次/分钟\n")
            avg_blink_duration = self.get_avg_blink_duration()
            f.write(f"平均眨眼持续时间: {avg_blink_duration} 毫秒\n\n")

            # 打哈欠统计
            f.write("打哈欠统计:\n")
            f.write("-" * 30 + "\n")
            f.write(f"总打哈欠次数: {self.get_yawn_count()} 次\n")
            if detection_duration > 0:
                yawns_per_minute = self.get_yawn_count() / (detection_duration / 60)
                f.write(f"平均每分钟打哈欠次数: {yawns_per_minute:.2f} 次/分钟\n\n")

            # PERCLOS统计
            f.write("PERCLOS统计:\n")
            f.write("-" * 30 + "\n")
            if self.perclos_history:
                avg_perclos = sum(self.perclos_history) / len(self.perclos_history)
                max_perclos = max(self.perclos_history)
                f.write(f"平均PERCLOS值: {avg_perclos:.4f}\n")
                f.write(f"最大PERCLOS值: {max_perclos:.4f}\n\n")

            # 闭眼持续时间统计
            f.write("闭眼持续时间统计:\n")
            f.write("-" * 30 + "\n")
            f.write(f"最大闭眼持续时间: {self.get_max_eye_closed_duration():.2f} 秒\n\n")

            # 疲劳警报统计
            f.write("疲劳警报统计:\n")
            f.write("-" * 30 + "\n")
            if self.alert_level_history:
                alert_counts = [0, 0, 0]  # 正常、轻度警告、严重警告
                for level in self.alert_level_history:
                    if 0 <= level < len(alert_counts):
                        alert_counts[level] += 1
                total_alerts = sum(alert_counts)
                if total_alerts > 0:
                    normal_percent = alert_counts[0] / total_alerts * 100
                    mild_percent = alert_counts[1] / total_alerts * 100
                    severe_percent = alert_counts[2] / total_alerts * 100
                    f.write(f"正常状态时间占比: {normal_percent:.2f}%\n")
                    f.write(f"轻度疲劳警告时间占比: {mild_percent:.2f}%\n")
                    f.write(f"严重疲劳警告时间占比: {severe_percent:.2f}%\n\n")

            # 复合疲劳事件
            f.write("复合疲劳事件记录:\n")
            f.write("-" * 30 + "\n")
            fatigue_events = self.get_compound_fatigue_events()
            if fatigue_events:
                for event, timestamp in fatigue_events:
                    event_time = timestamp - self.detection_start_time
                    f.write(f"{event}: 发生于检测后 {event_time:.2f} 秒\n")
            else:
                f.write("未检测到复合疲劳事件\n\n")

            # 检测性能统计
            f.write("检测性能统计:\n")
            f.write("-" * 30 + "\n")
            f.write(f"平均每帧处理时间: {self.get_avg_process_time()} 毫秒\n")
            if hasattr(self, 'video_fps') and self.video_fps:
                f.write(f"视频帧率: {self.video_fps} fps\n")
            f.write(f"检测模式: {self.get_detection_mode()}\n")

        return True


def main():
    """主函数，用于测试疲劳检测器"""
    import argparse

    # 创建命令行参数解析器
    parser = argparse.ArgumentParser(description='驾驶员疲劳检测')
    parser.add_argument('--model', type=str,
                        default=r"E:\b-s\yolov10\yolov10-main\runs\detect\train_v1010\weights\best.pt",
                        help='YOLO模型路径')
    parser.add_argument('--source', type=str, default=r"E:\b-s\yolov10\yolov10-main\saki\2-MaleGlasses.avi",
                        help='视频源，可以是视频文件路径或摄像头ID(0表示默认摄像头)')
    parser.add_argument('--output', type=str, default=r'E:\b-s\yolov10\yolov10-main\saki\output.mp4',
                        help='输出视频路径')
    parser.add_argument('--performance', type=str, default='balanced',
                        choices=['high_speed', 'balanced', 'high_accuracy'], help='性能模式')
    parser.add_argument('--mode', type=str, default='combined',
                        choices=['combined', 'eyes_only', 'mouth_only'], help='检测模式')
    parser.add_argument('--camera_mode', action='store_true', help='启用摄像头优化模式')
    parser.add_argument('--low_light', action='store_true', help='启用低光增强')
    parser.add_argument('--retinex_model', type=str, default=r'E:\b-s\yolov10\yolov10-main\saki\Epoch99.pth',
                        help='RetinexNet模型路径')
    # 添加性能监控参数
    parser.add_argument('--monitor', action='store_true', help='启用性能监控')
    parser.add_argument('--monitor_interval', type=int, default=100, help='性能监控报告间隔(帧数)')

    args = parser.parse_args()

    # 初始化疲劳检测器
    detector = FatigueDetector(args.model)

    # 设置性能模式
    detector.set_performance_mode(args.performance)

    # 设置检测模式
    if args.mode == 'eyes_only':
        detector.set_detection_mode(detect_eyes=True, detect_mouth=False)
    elif args.mode == 'mouth_only':
        detector.set_detection_mode(detect_eyes=False, detect_mouth=True)
    else:
        detector.set_detection_mode(detect_eyes=True, detect_mouth=True)

    # 如果需要，启用低光增强
    if args.low_light:
        detector.toggle_low_light_enhancement(True, args.retinex_model)
        print(f"已启用低光增强，使用模型: {args.retinex_model}")

    # 如果是摄像头模式，启用摄像头优化
    is_camera = args.source.isdigit() or args.camera_mode
    if is_camera:
        detector.optimize_for_camera(True)
        detector.set_camera_resolution(640, 480)

    # 打开视频源
    if args.source.isdigit():
        cap = cv2.VideoCapture(int(args.source))
        # 为摄像头设置分辨率
        cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
        cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
        # 设置缓冲区大小为1，避免帧堆积
        cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
    else:
        cap = cv2.VideoCapture(args.source)

    if not cap.isOpened():
        print(f"无法打开视频源: {args.source}")
        return

    # 获取视频信息
    frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
    fps = cap.get(cv2.CAP_PROP_FPS)

    # 设置视频帧率
    detector.video_fps = fps

    # 设置输出视频
    out = None
    if args.output:
        fourcc = cv2.VideoWriter_fourcc(*'mp4v')
        out = cv2.VideoWriter(args.output, fourcc, fps, (frame_width, frame_height))

    # 开始处理视频
    frame_count = 0
    start_time = time.time()
    last_monitor_time = start_time  # 用于计算实时帧率

    # 帧率控制变量
    target_fps = 30.0  # 目标处理帧率
    frame_interval = 1.0 / target_fps
    last_frame_time = time.time()

    # 性能监控变量
    performance_frames = 0  # 性能监控周期内处理的帧数

    while True:
        # 帧率控制
        current_time = time.time()
        elapsed = current_time - last_frame_time

        if elapsed < frame_interval:
            # 如果距离上一帧处理时间不足，等待一小段时间
            time.sleep(max(0, frame_interval - elapsed))

        # 更新上一帧时间
        last_frame_time = time.time()

        ret, frame = cap.read()

        if not ret:
            break

        # 处理帧
        processed_frame = detector.process_frame(frame)

        # 显示处理后的帧
        cv2.imshow('Fatigue Detection', processed_frame)

        # 写入输出视频
        if out is not None:
            out.write(processed_frame)

        # 按ESC键退出
        if cv2.waitKey(1) & 0xFF == 27:
            break

        frame_count += 1
        performance_frames += 1

        # 性能监控
        if args.monitor and frame_count % args.monitor_interval == 0:
            # 计算实时帧率
            current_time = time.time()
            interval = current_time - last_monitor_time
            if interval > 0:
                real_time_fps = performance_frames / interval
                print(f"\n实时帧率: {real_time_fps:.2f} FPS (最近{args.monitor_interval}帧)")

                # 重置性能监控计数器
                performance_frames = 0
                last_monitor_time = current_time

                # 输出详细性能指标
                metrics = detector.report_performance_metrics()

                # 显示内存使用情况
                import psutil
                process = psutil.Process()
                memory_info = process.memory_info()
                print(f"内存使用: {memory_info.rss / (1024 * 1024):.1f} MB")
                print(f"CPU使用率: {psutil.cpu_percent()}%")

                # 如果是摄像头模式，显示缓冲区信息
                if is_camera:
                    print(f"摄像头缓冲帧数: {int(cap.get(cv2.CAP_PROP_BUFFERSIZE))}")

    # 计算处理帧率
    elapsed_time = time.time() - start_time
    if elapsed_time > 0:
        fps = frame_count / elapsed_time
        print(f"\n总体平均处理帧率: {fps:.2f} FPS")
        print(f"总处理帧数: {frame_count}")
        print(f"总处理时间: {elapsed_time:.2f} 秒")

    # 在退出前输出最终性能报告
    if args.monitor:
        print("\n===== 最终性能报告 =====")
        detector.report_performance_metrics()

    # 释放资源
    cap.release()
    if out is not None:
        out.release()
    cv2.destroyAllWindows()

    # 生成报告
    report_folder = os.path.splitext(args.output)[0] + "_report" if args.output else "fatigue_detection_report"
    detector.generate_report(report_folder)
    print(f"检测报告已生成至: {report_folder}")


if __name__ == "__main__":
    main()

